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_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 \
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)

View File

@@ -17,6 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <fstream>
#include <iostream>
#include <istream>
@@ -81,7 +82,7 @@ void benchmark(const Position& current, istream& is) {
vector<string> fens;
// 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 limit = (is >> token) ? token : "13";
string fenFile = (is >> token) ? token : "default";
@@ -137,22 +138,9 @@ void benchmark(const Position& current, istream& is) {
cerr << "\nPosition: " << i + 1 << '/' << fens.size() << endl;
if (limitType == "divide")
for (MoveList<LEGAL> it(pos); *it; ++it)
{
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;
}
if (limitType == "perft")
nodes += Search::perft<true>(pos, limits.depth * ONE_PLY);
else
{
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

View File

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

View File

@@ -149,7 +149,10 @@ const std::string Bitboards::pretty(Bitboard b) {
void Bitboards::init() {
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)
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[],
Bitboard masks[], unsigned shifts[], Square deltas[], Fn index) {
int MagicBoosters[][8] = { { 969, 1976, 2850, 542, 2069, 2852, 1708, 164 },
{ 3101, 552, 3555, 926, 834, 26, 2131, 1117 } };
int MagicBoosters[][RANK_NB] = { { 969, 1976, 2850, 542, 2069, 2852, 1708, 164 },
{ 3101, 552, 3555, 926, 834, 26, 2131, 1117 } };
RKISS rk;
Bitboard occupancy[4096], reference[4096], edges, b;
int i, size, booster;
@@ -301,7 +303,8 @@ namespace {
// Find a magic for square 's' picking up an (almost) random number
// until we find the one that passes the verification test.
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);
std::memset(attacks[s], 0, size * sizeof(Bitboard));

View File

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

View File

@@ -96,8 +96,7 @@ inline int popcount<CNT_HW_POPCNT>(Bitboard b) {
#else
__asm__("popcnt %1, %0" : "=r" (b) : "r" (b));
return b;
return __builtin_popcountll(b);
#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 {
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];
@@ -89,23 +89,15 @@ namespace {
std::string do_trace(const Position& pos);
}
// Evaluation weights, initialized from UCI options
enum { Mobility, PawnStructure, PassedPawns, Space, KingDangerUs, KingDangerThem };
struct Weight { int mg, eg; } Weights[6];
// Evaluation weights, indexed by evaluation term
enum { Mobility, PawnStructure, PassedPawns, Space, KingSafety };
const struct Weight { int mg, eg; } Weights[] = {
{289, 344}, {233, 201}, {221, 273}, {46, 0}, {318, 0}
};
typedef Value V;
#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
// game, indexed by piece type and number of attacked squares not occupied by
// friendly pieces.
@@ -155,27 +147,27 @@ namespace {
// ThreatenedByPawn[PieceType] contains a penalty according to which piece
// type is attacked by an enemy pawn.
const Score ThreatenedByPawn[] = {
S(0, 0), S(0, 0), S(56, 70), S(56, 70), S(76, 99), S(86, 118)
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
const Score Hanging[2] = { S(23, 20) , S(35, 45) };
#undef S
const Score Tempo = make_score(24, 11);
const Score RookOnPawn = make_score(10, 28);
const Score RookOpenFile = make_score(43, 21);
const Score RookSemiopenFile = make_score(19, 10);
const Score BishopPawns = make_score( 8, 12);
const Score MinorBehindPawn = make_score(16, 0);
const Score TrappedRook = make_score(90, 0);
const Score Unstoppable = make_score( 0, 20);
// Assorted bonuses and penalties used by evaluation
const Score KingOnOne = S(2 , 58);
const Score KingOnMany = S(6 ,125);
const Score RookOnPawn = S(10, 28);
const Score RookOpenFile = S(43, 21);
const Score RookSemiOpenFile = S(19, 10);
const Score BishopPawns = S( 8, 12);
const Score MinorBehindPawn = S(16, 0);
const Score TrappedRook = S(92, 0);
const Score Unstoppable = S( 0, 20);
const Score Hanging = S(23, 20);
// Penalty for a bishop on a1/h1 (a8/h8 for black) which is trapped by
// a friendly pawn on b2/g2 (b7/g7 for black). This can obviously only
// happen in Chess960 games.
const Score TrappedBishopA1H1 = make_score(50, 50);
const Score TrappedBishopA1H1 = S(50, 50);
#undef S
// SpaceMask[Color] contains the area of the board which is considered
// by the space evaluation. In the middlegame, each side is given a bonus
@@ -202,10 +194,11 @@ namespace {
const int BishopCheck = 2;
const int KnightCheck = 3;
// KingDanger[Color][attackUnits] contains the actual king danger weighted
// scores, indexed by color and by a calculated integer number.
Score KingDanger[COLOR_NB][128];
// KingDanger[attackUnits] contains the actual king danger weighted
// scores, indexed by a calculated integer number.
Score KingDanger[128];
const int ScalePawnSpan[2] = { 38, 56 };
// apply_weight() weighs score 'v' by weight 'w' trying to prevent overflow
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
// 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);
// 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);
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>
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);
@@ -274,7 +256,7 @@ namespace {
bonus += bonus / 2;
}
return make_score(bonus, bonus);
return make_score(bonus * 2, bonus / 2);
}
@@ -335,9 +317,9 @@ namespace {
if (Pt == BISHOP)
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)))
score += evaluate_outposts<Pt, Us>(pos, ei, s);
score += evaluate_outpost<Pt, Us>(pos, ei, s);
// Bishop or knight behind a pawn
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
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)))
continue;
@@ -369,7 +351,7 @@ namespace {
if ( ((file_of(ksq) < FILE_E) == (file_of(s) < file_of(ksq)))
&& (rank_of(ksq) == rank_of(s) || relative_rank(Us, ksq) == RANK_1)
&& !ei.pi->semiopen_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
@@ -432,7 +414,8 @@ namespace {
attackUnits = std::min(20, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2)
+ 3 * (ei.kingAdjacentZoneAttacksCount[Them] + popcount<Max15>(undefended))
+ 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
// 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]);
if (b)
attackUnits += QueenContactCheck
* popcount<Max15>(b)
* (Them == pos.side_to_move() ? 2 : 1);
attackUnits += QueenContactCheck * popcount<Max15>(b);
}
// Analyse the enemy's safe rook contact checks. Firstly, find the
@@ -465,9 +446,7 @@ namespace {
| ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][QUEEN]);
if (b)
attackUnits += RookContactCheck
* popcount<Max15>(b)
* (Them == pos.side_to_move() ? 2 : 1);
attackUnits += RookContactCheck * popcount<Max15>(b);
}
// 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[]
// array and subtract the score from evaluation.
score -= KingDanger[Us == Search::RootColor][attackUnits];
score -= KingDanger[attackUnits];
}
if (Trace)
@@ -519,8 +498,17 @@ namespace {
const Color Them = (Us == WHITE ? BLACK : WHITE);
Bitboard b, weakEnemies;
Bitboard b, weakEnemies, protectedEnemies;
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
weakEnemies = pos.pieces(Them)
@@ -530,18 +518,21 @@ namespace {
// Add a bonus according if the attacking pieces are minor or major
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)
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]);
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];
if (b)
score += more_than_one(b) ? Hanging[Us != pos.side_to_move()] * popcount<Max15>(b)
: Hanging[Us == pos.side_to_move()];
score += more_than_one(b) ? Hanging * popcount<Max15>(b) : Hanging;
b = weakEnemies & ei.attackedBy[Us][KING];
if (b)
score += more_than_one(b) ? KingOnMany : KingOnOne;
}
if (Trace)
@@ -590,22 +581,18 @@ namespace {
// If the pawn is free to advance, then increase the bonus
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,
// 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));
Bitboard bb = forward_bb(Them, s) & pos.pieces(ROOK, QUEEN) & pos.attacks_from<ROOK>(s);
if ( unlikely(forward_bb(Them, s) & pos.pieces(Us, ROOK, QUEEN))
&& (forward_bb(Them, s) & pos.pieces(Us, ROOK, QUEEN) & pos.attacks_from<ROOK>(s)))
defendedSquares = squaresToQueen;
else
defendedSquares = squaresToQueen & ei.attackedBy[Us][ALL_PIECES];
if (!(pos.pieces(Us) & bb))
defendedSquares &= ei.attackedBy[Us][ALL_PIECES];
if (!(pos.pieces(Them) & bb))
unsafeSquares &= ei.attackedBy[Them][ALL_PIECES] | pos.pieces(Them);
// If there aren't any enemy attacks, assign a big bonus. Otherwise
// assign a smaller bonus if the block square isn't attacked.
@@ -621,6 +608,8 @@ namespace {
mbonus += k * rr, ebonus += k * rr;
}
else if (pos.pieces(Us) & blockSq)
mbonus += rr * 3 + r * 2 + 3, ebonus += rr + r * 2;
} // rr != 0
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
// candidate pawns. In case opponent has no pieces but pawns, this is somewhat
// related to the possibility that pawns are unstoppable.
// evaluate_unstoppable_pawns() scores the most advanced passed pawn. In case
// both players have no pieces but pawns, this is somewhat related to the
// 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 SCORE_ZERO;
return Unstoppable * int(relative_rank(us, frontmost_sq(us, b)));
return b ? Unstoppable * int(relative_rank(us, frontmost_sq(us, b))) : SCORE_ZERO;
}
@@ -696,9 +682,9 @@ namespace {
Thread* thisThread = pos.this_thread();
// Initialize score by reading the incrementally updated scores included
// in the position object (material + piece square tables) and adding a
// Tempo bonus. Score is computed from the point of view of white.
score = pos.psq_score() + (pos.side_to_move() == WHITE ? Tempo : -Tempo);
// in the position object (material + piece square tables).
// Score is computed from the point of view of white.
score = pos.psq_score();
// Probe the material hash table
ei.mi = Material::probe(pos, thisThread->materialTable, thisThread->endgames);
@@ -741,10 +727,10 @@ namespace {
score += evaluate_passed_pawns<WHITE, Trace>(pos, ei)
- evaluate_passed_pawns<BLACK, Trace>(pos, ei);
// If one side has only a king, score for potential unstoppable pawns
if (!pos.non_pawn_material(WHITE) || !pos.non_pawn_material(BLACK))
score += evaluate_unstoppable_pawns(pos, WHITE, ei)
- evaluate_unstoppable_pawns(pos, BLACK, ei);
// If both sides have only pawns, score for potential unstoppable pawns
if (!pos.non_pawn_material(WHITE) && !pos.non_pawn_material(BLACK))
score += evaluate_unstoppable_pawns(WHITE, ei)
- evaluate_unstoppable_pawns(BLACK, ei);
// Evaluate space for both sides, only in middlegame
if (ei.mi->space_weight())
@@ -754,29 +740,35 @@ namespace {
}
// Scale winning side if position is more drawish than it appears
ScaleFactor sf = eg_value(score) > VALUE_DRAW ? ei.mi->scale_factor(pos, WHITE)
: ei.mi->scale_factor(pos, BLACK);
Color strongSide = eg_value(score) > VALUE_DRAW ? WHITE : BLACK;
ScaleFactor sf = ei.mi->scale_factor(pos, strongSide);
// If we don't already have an unusual scale factor, check for opposite
// colored bishop endgames, and use a lower scale for those.
// If we don't already have an unusual scale factor, check for certain
// types of endgames, and use a lower scale for those.
if ( ei.mi->game_phase() < PHASE_MIDGAME
&& pos.opposite_bishops()
&& (sf == SCALE_FACTOR_NORMAL || sf == SCALE_FACTOR_ONEPAWN))
{
// Ignoring any pawns, do both sides only have a single bishop and no
// other pieces?
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.
bool one_pawn = (pos.count<PAWN>(WHITE) + pos.count<PAWN>(BLACK) == 1);
sf = one_pawn ? ScaleFactor(8) : ScaleFactor(32);
if (pos.opposite_bishops()) {
// Ignoring any pawns, do both sides only have a single bishop and no
// other pieces?
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.
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
@@ -788,7 +780,7 @@ namespace {
// In case of tracing add all single evaluation contributions for both white and black
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(PAWN, ei.pi->pawns_value());
Tracing::add_term(Tracing::MOBILITY, apply_weight(mobility[WHITE], Weights[Mobility])
@@ -821,13 +813,13 @@ namespace {
Score bScore = terms[BLACK][idx];
switch (idx) {
case PST: case IMBALANCE: case PAWN: case TOTAL:
ss << std::setw(20) << name << " | --- --- | --- --- | "
case MATERIAL: case IMBALANCE: case PAWN: case TOTAL:
ss << std::setw(15) << name << " | --- --- | --- --- | "
<< std::setw(5) << to_cp(mg_value(wScore - bScore)) << " "
<< std::setw(5) << to_cp(eg_value(wScore - bScore)) << " \n";
break;
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(eg_value(wScore)) << " | "
<< std::setw(5) << to_cp(mg_value(bScore)) << " "
@@ -846,12 +838,12 @@ namespace {
std::stringstream ss;
ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2)
<< " Eval term | White | Black | Total \n"
<< " | MG EG | MG EG | MG EG \n"
<< "---------------------+-------------+-------------+-------------\n";
<< " Eval term | White | Black | Total \n"
<< " | MG EG | MG EG | MG EG \n"
<< "----------------+-------------+-------------+-------------\n";
format_row(ss, "Material, PST, Tempo", PST);
format_row(ss, "Material imbalance", IMBALANCE);
format_row(ss, "Material", MATERIAL);
format_row(ss, "Imbalance", IMBALANCE);
format_row(ss, "Pawns", PAWN);
format_row(ss, "Knights", KNIGHT);
format_row(ss, "Bishops", BISHOP);
@@ -863,7 +855,7 @@ namespace {
format_row(ss, "Passed pawns", PASSED);
format_row(ss, "Space", SPACE);
ss << "---------------------+-------------+-------------+-------------\n";
ss << "----------------+-------------+-------------+-------------\n";
format_row(ss, "Total", TOTAL);
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.
Value evaluate(const Position& pos) {
return do_evaluate<false>(pos);
return do_evaluate<false>(pos) + Tempo;
}
@@ -898,22 +890,13 @@ namespace Eval {
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 Peak = 1280;
for (int t = 0, i = 1; i < 100; ++i)
{
t = int(std::min(Peak, std::min(0.4 * i * i, t + MaxSlope)));
KingDanger[1][i] = apply_weight(make_score(t, 0), Weights[KingDangerUs]);
KingDanger[0][i] = apply_weight(make_score(t, 0), Weights[KingDangerThem]);
KingDanger[i] = apply_weight(make_score(t, 0), Weights[KingSafety]);
}
}

View File

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

View File

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

View File

@@ -29,10 +29,10 @@ namespace {
// Polynomial material balance parameters
// pair pawn knight bishop rook queen
const int LinearCoefficients[6] = { 1852, -162, -1122, -183, 249, -154 };
// pair pawn knight bishop rook queen
const int Linear[6] = { 1852, -162, -1122, -183, 249, -154 };
const int QuadraticCoefficientsSameSide[][PIECE_TYPE_NB] = {
const int QuadraticSameSide[][PIECE_TYPE_NB] = {
// OUR PIECES
// pair pawn knight bishop rook queen
{ 0 }, // Bishop pair
@@ -43,7 +43,7 @@ namespace {
{-177, 25, 129, 142, -137, 0 } // Queen
};
const int QuadraticCoefficientsOppositeSide[][PIECE_TYPE_NB] = {
const int QuadraticOppositeSide[][PIECE_TYPE_NB] = {
// THEIR PIECES
// pair pawn knight bishop rook queen
{ 0 }, // Bishop pair
@@ -66,8 +66,7 @@ namespace {
// Helper templates used to detect a given material distribution
template<Color Us> bool is_KXK(const Position& pos) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
return !pos.count<PAWN>(Them)
&& pos.non_pawn_material(Them) == VALUE_ZERO
return !more_than_one(pos.pieces(Them))
&& pos.non_pawn_material(Us) >= RookValueMg;
}
@@ -94,26 +93,24 @@ namespace {
const Color Them = (Us == WHITE ? BLACK : WHITE);
int pt1, pt2, pc, v;
int value = 0;
int bonus = 0;
// 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 (!pc)
if (!pieceCount[Us][pt1])
continue;
v = LinearCoefficients[pt1];
int v = Linear[pt1];
for (pt2 = NO_PIECE_TYPE; pt2 <= pt1; ++pt2)
v += QuadraticCoefficientsSameSide[pt1][pt2] * pieceCount[Us][pt2]
+ QuadraticCoefficientsOppositeSide[pt1][pt2] * pieceCount[Them][pt2];
for (int pt2 = NO_PIECE_TYPE; pt2 <= pt1; ++pt2)
v += QuadraticSameSide[pt1][pt2] * pieceCount[Us][pt2]
+ QuadraticOppositeSide[pt1][pt2] * pieceCount[Them][pt2];
value += pc * v;
bonus += pieceCount[Us][pt1] * v;
}
return value;
return bonus;
}
} // namespace
@@ -139,7 +136,7 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
std::memset(e, 0, sizeof(Entry));
e->key = key;
e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL;
e->gamePhase = game_phase(pos);
e->gamePhase = pos.game_phase();
// Let's look if we have a specialized evaluation function for this particular
// 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;
}
/// 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

View File

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

View File

@@ -17,6 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
@@ -28,7 +29,7 @@ using namespace std;
/// Version number. If Version is left empty, then compile date in the format
/// 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

View File

@@ -20,7 +20,7 @@
#ifndef MISC_H_INCLUDED
#define MISC_H_INCLUDED
#include <fstream>
#include <ostream>
#include <string>
#include <vector>
@@ -37,12 +37,6 @@ extern void dbg_mean_of(int v);
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 {
typedef int64_t point;
inline point now() { return system_time_to_msec(); }

View File

@@ -18,9 +18,7 @@
*/
#include <cassert>
#include <iomanip>
#include <sstream>
#include <stack>
#include "movegen.h"
#include "notation.h"
@@ -42,7 +40,7 @@ string score_to_uci(Value v, Value alpha, Value beta) {
stringstream ss;
if (abs(v) < VALUE_MATE_IN_MAX_PLY)
if (abs(v) < VALUE_MATE - MAX_PLY)
ss << "cp " << v * 100 / PawnValueEg;
else
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;
}
/// 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);
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_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

View File

@@ -49,13 +49,13 @@ namespace {
{ S(20, 28), S(29, 31), S(33, 31), S(33, 31),
S(33, 31), S(33, 31), S(29, 31), S(20, 28) } };
// Connected pawn bonus by file and rank (initialized by formula)
Score Connected[FILE_NB][RANK_NB];
// Connected bonus by rank
const int Connected[RANK_NB] = {0, 6, 15, 10, 57, 75, 135, 258};
// Candidate passed pawn bonus by rank
const Score CandidatePassed[RANK_NB] = {
S( 0, 0), S( 6, 13), S(6,13), S(14,29),
S(34,68), S(83,166), S(0, 0), S( 0, 0) };
// Levers bonus by rank
const Score Lever[RANK_NB] = {
S( 0, 0), S( 0, 0), 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
const Score PawnsFileSpan = S(0, 15);
@@ -69,7 +69,7 @@ namespace {
// Danger of enemy pawns moving toward our king indexed by
// [no friendly pawn | pawn unblocked | pawn blocked][rank of enemy pawn]
const Value StormDanger[3][RANK_NB] = {
const Value StormDanger[][RANK_NB] = {
{ V( 0), V(64), V(128), V(51), V(26) },
{ V(26), V(32), V( 96), V(38), V(20) },
{ V( 0), V( 0), V(160), V(25), V(13) } };
@@ -91,15 +91,15 @@ namespace {
Bitboard b, p, doubled;
Square s;
File f;
bool passed, isolated, opposed, connected, backward, candidate, unsupported;
bool passed, isolated, opposed, connected, backward, unsupported, lever;
Score value = SCORE_ZERO;
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);
e->passedPawns[Us] = e->candidatePawns[Us] = 0;
e->passedPawns[Us] = 0;
e->kingSquares[Us] = SQ_NONE;
e->semiopenFiles[Us] = 0xFF;
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));
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
e->semiopenFiles[Us] &= ~(1 << f);
@@ -130,6 +131,7 @@ namespace {
doubled = ourPawns & forward_bb(Us, s);
opposed = theirPawns & forward_bb(Us, s);
passed = !(theirPawns & passed_pawn_mask(Us, s));
lever = theirPawns & pawnAttacksBB[s];
// Test for backward pawn.
// 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));
// 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
// full attack info to evaluate passed pawns. Only the frontmost passed
// pawn on each file is considered a true passed pawn.
@@ -182,25 +176,23 @@ namespace {
if (backward)
value -= Backward[opposed][f];
if (connected)
value += Connected[f][relative_rank(Us, s)];
if (candidate)
{
value += CandidatePassed[relative_rank(Us, s)];
if (!doubled)
e->candidatePawns[Us] |= s;
if (connected) {
int bonus = Connected[rr];
if (ourPawns & adjacent_files_bb(f) & rank_bb(r))
bonus += (Connected[rr+1] - Connected[rr]) / 2;
value += make_score(bonus / 2, bonus >> opposed);
}
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
// to file distance between left and right outermost pawns.
if (pos.count<PAWN>(Us) > 1)
{
b = e->semiopenFiles[Us] ^ 0xFF;
value += PawnsFileSpan * int(msb(b) - lsb(b));
}
value += PawnsFileSpan * e->pawnSpan[Us];
return value;
}
@@ -209,24 +201,8 @@ namespace {
namespace Pawns {
/// init() initializes some tables by formula instead of hard-coding their values
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
/// 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
/// to recompute everything when the same pawn structure occurs again.
Entry* probe(const Position& pos, Table& entries) {
@@ -250,30 +226,30 @@ template<Color Us>
Value Entry::shelter_storm(const Position& pos, Square ksq) {
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 ourPawns = b & pos.pieces(Us);
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)));
for (File f = kf - File(1); f <= kf + File(1); ++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);
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
&& relative_rank(Us, ksq) == rkThem - 1)
safety += 200;
else
safety -= ShelterWeakness[rkUs]
+ StormDanger[rkUs == RANK_1 ? 0 : rkThem == rkUs + 1 ? 2 : 1][rkThem];
safety -= ShelterWeakness[rkUs]
+ StormDanger[rkUs == RANK_1 ? 0 :
rkThem != rkUs + 1 ? 1 : 2][rkThem];
}
return safety;

View File

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

View File

@@ -226,7 +226,7 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
incremented after Black's move.
*/
char col, row, token;
unsigned char col, row, token;
size_t idx;
Square sq = SQ_A8;
std::istringstream ss(fenStr);
@@ -430,17 +430,12 @@ const string Position::fen() const {
}
/// Position::pretty() returns an ASCII representation of the position to be
/// printed to the standard output together with the move's san notation.
/// Position::pretty() returns an ASCII representation of the position
const string Position::pretty(Move m) const {
const string Position::pretty() const {
std::ostringstream ss;
if (m)
ss << "\nMove: " << (sideToMove == BLACK ? ".." : "")
<< move_to_san(*const_cast<Position*>(this), m);
ss << "\n +---+---+---+---+---+---+---+---+\n";
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; )
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();
}
/// 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
/// '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
@@ -803,9 +807,6 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
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
if (type_of(m) != CASTLING)
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
/// material gain or loss resulting from a move.
@@ -1113,10 +1134,6 @@ Value Position::see(Move m) 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()))
return true;

View File

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

View File

@@ -19,18 +19,18 @@
#include <algorithm>
#include <cassert>
#include <cfloat>
#include <cmath>
#include <cstring>
#include <iostream>
#include <sstream>
#include "book.h"
#include "evaluate.h"
#include "movegen.h"
#include "movepick.h"
#include "notation.h"
#include "rkiss.h"
#include "search.h"
#include "tbprobe.h"
#include "timeman.h"
#include "thread.h"
#include "tt.h"
@@ -42,9 +42,14 @@ namespace Search {
LimitsType Limits;
std::vector<RootMove> RootMoves;
Position RootPos;
Color RootColor;
Time::point SearchTime;
StateStackPtr SetupStates;
int TBCardinality;
uint64_t TBHits;
bool RootInTB;
bool TB50MoveRule;
Depth TBProbeDepth;
Value TBScore;
}
using std::string;
@@ -53,31 +58,27 @@ using namespace Search;
namespace {
// Set to true to force running with one thread. Used for debugging
const bool FakeSplit = false;
// Different node types, used as template parameter
enum NodeType { Root, PV, NonPV };
// 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
int FutilityMoveCounts[2][32]; // [improving][depth]
inline Value futility_margin(Depth d) {
return Value(100 * d);
return Value(200 * d);
}
// Reduction lookup tables (initialized at startup) and their access function
int8_t Reductions[2][2][64][64]; // [pv][improving][depth][moveNumber]
template <bool PvNode> inline Depth reduction(bool i, Depth d, int mn) {
return (Depth) Reductions[PvNode][i][std::min(int(d) / ONE_PLY, 63)][std::min(mn, 63)];
return (Depth) Reductions[PvNode][i][std::min(int(d), 63)][std::min(mn, 63)];
}
size_t MultiPV, PVIdx;
size_t PVIdx;
TimeManager TimeMgr;
double BestMoveChanges;
Value DrawValue[COLOR_NB];
@@ -98,18 +99,21 @@ namespace {
string uci_pv(const Position& pos, int depth, Value alpha, Value beta);
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() {
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(),
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; }
Move pick_move();
int level;
size_t candidates;
Move best;
};
@@ -129,50 +133,55 @@ void Search::init() {
{
double pvRed = 0.00 + log(double(hd)) * log(double(mc)) / 3.00;
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[0][0][hd][mc] = Reductions[0][1][hd][mc];
if (Reductions[0][0][hd][mc] > 2 * ONE_PLY)
Reductions[0][0][hd][mc] += ONE_PLY;
else if (Reductions[0][0][hd][mc] > 1 * ONE_PLY)
Reductions[0][0][hd][mc] += ONE_PLY / 2;
if (Reductions[0][0][hd][mc] >= 2)
Reductions[0][0][hd][mc] += 1;
}
// Init futility move count array
for (d = 0; d < 32; ++d)
{
FutilityMoveCounts[0][d] = int(2.4 + 0.222 * pow(d + 0.00, 1.8));
FutilityMoveCounts[1][d] = int(3.0 + 0.300 * pow(d + 0.98, 1.8));
FutilityMoveCounts[0][d] = int(2.4 + 0.773 * pow(d + 0.00, 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
/// up to the given depth are generated and counted and the sum returned.
static uint64_t perft(Position& pos, Depth depth) {
template<bool Root>
uint64_t Search::perft(Position& pos, Depth depth) {
StateInfo st;
uint64_t cnt = 0;
uint64_t cnt, nodes = 0;
CheckInfo ci(pos);
const bool leaf = depth == 2 * ONE_PLY;
const bool leaf = (depth == 2 * ONE_PLY);
for (MoveList<LEGAL> it(pos); *it; ++it)
{
pos.do_move(*it, st, ci, pos.gives_check(*it, ci));
cnt += leaf ? MoveList<LEGAL>(pos).size() : ::perft(pos, depth - ONE_PLY);
pos.undo_move(*it);
if (Root && depth <= ONE_PLY)
cnt = 1, nodes++;
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) {
return depth > ONE_PLY ? ::perft(pos, depth) : MoveList<LEGAL>(pos).size();
}
template uint64_t Search::perft<true>(Position& pos, Depth depth);
/// 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
@@ -180,14 +189,14 @@ uint64_t Search::perft(Position& pos, Depth depth) {
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();
TimeMgr.init(Limits, RootPos.game_ply(), RootColor);
int cf = Options["Contempt Factor"] * PawnValueEg / 100; // From centipawns
DrawValue[ RootColor] = VALUE_DRAW - Value(cf);
DrawValue[~RootColor] = VALUE_DRAW + Value(cf);
int cf = Options["Contempt"] * PawnValueEg / 100; // From centipawns
DrawValue[ RootPos.side_to_move()] = VALUE_DRAW - Value(cf);
DrawValue[~RootPos.side_to_move()] = VALUE_DRAW + Value(cf);
if (RootMoves.empty())
{
@@ -199,27 +208,55 @@ void Search::think() {
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"]);
if (bookMove && std::count(RootMoves.begin(), RootMoves.end(), bookMove))
{
std::swap(RootMoves[0], *std::find(RootMoves.begin(), RootMoves.end(), bookMove));
goto finalize;
}
TBCardinality = Tablebases::TBLargest;
TBProbeDepth = 0 * ONE_PLY;
}
TB50MoveRule = Options["Syzygy50MoveRule"];
if (Options["Write Search Log"])
if (piecesCnt <= TBCardinality)
{
Log log(Options["Search Log Filename"]);
log << "\nSearching: " << RootPos.fen()
<< "\ninfinite: " << Limits.infinite
<< " ponder: " << Limits.ponder
<< " time: " << Limits.time[RootColor]
<< " increment: " << Limits.inc[RootColor]
<< " moves to go: " << Limits.movestogo
<< "\n" << std::endl;
TBHits = RootMoves.size();
// If the current root position is in the tablebases then RootMoves
// contains only moves that preserve the draw or win.
RootInTB = Tablebases::root_probe(RootPos, TBScore);
if (RootInTB)
{
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
@@ -233,25 +270,16 @@ void Search::think() {
Threads.timer->run = false; // Stop the timer
if (Options["Write Search Log"])
if (RootInTB)
{
Time::point elapsed = Time::now() - SearchTime + 1;
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]);
// If we mangled the hash key, unmangle it here
}
finalize:
// When search is stopped this info is not printed
sync_cout << "info nodes " << RootPos.nodes_searched()
<< " tbhits " << TBHits
<< " time " << Time::now() - SearchTime + 1 << sync_endl;
// When we reach the maximum depth, we can arrive here without a raise of
@@ -285,7 +313,6 @@ namespace {
Value bestValue, alpha, beta, delta;
std::memset(ss-2, 0, 5 * sizeof(Stack));
(ss-1)->currentMove = MOVE_NULL; // Hack to skip update gains
depth = 0;
BestMoveChanges = 0;
@@ -298,15 +325,12 @@ namespace {
Countermoves.clear();
Followupmoves.clear();
MultiPV = Options["MultiPV"];
Skill skill(Options["Skill Level"]);
size_t multiPV = Options["MultiPV"];
Skill skill(Options["Skill Level"], RootMoves.size());
// 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.
if (skill.enabled() && MultiPV < 4)
MultiPV = 4;
MultiPV = std::min(MultiPV, RootMoves.size());
multiPV = std::max(multiPV, skill.candidates_size());
// Iterative deepening loop until requested to stop or target depth reached
while (++depth <= MAX_PLY && !Signals.stop && (!Limits.depth || depth <= Limits.depth))
@@ -320,7 +344,7 @@ namespace {
RootMoves[i].prevScore = RootMoves[i].score;
// 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
if (depth >= 5)
@@ -377,7 +401,7 @@ namespace {
else
break;
delta += delta / 2;
delta += 3 * delta / 8;
assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE);
}
@@ -385,25 +409,14 @@ namespace {
// Sort the PV lines searched so far and update the GUI
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;
}
// 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();
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"?
if ( Limits.mate
&& bestValue >= VALUE_MATE_IN_MAX_PLY
@@ -414,7 +427,7 @@ namespace {
if (Limits.use_time_management() && !Signals.stop && !Signals.stopOnPonderhit)
{
// 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);
// Stop the search if only one legal move is available or all
@@ -540,6 +553,37 @@ namespace {
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
if (inCheck)
{
@@ -560,7 +604,9 @@ namespace {
}
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);
}
@@ -568,6 +614,7 @@ namespace {
&& ss->staticEval != VALUE_NONE
&& (ss-1)->staticEval != VALUE_NONE
&& (move = (ss-1)->currentMove) != MOVE_NULL
&& move != MOVE_NONE
&& type_of(move) == NORMAL)
{
Square to = to_sq(move);
@@ -579,7 +626,6 @@ namespace {
&& depth < 4 * ONE_PLY
&& eval + razor_margin(depth) <= alpha
&& ttMove == MOVE_NONE
&& abs(beta) < VALUE_MATE_IN_MAX_PLY
&& !pos.pawn_on_7th(pos.side_to_move()))
{
if ( depth <= ONE_PLY
@@ -597,8 +643,7 @@ namespace {
&& !ss->skipNullMove
&& depth < 7 * ONE_PLY
&& eval - futility_margin(depth) >= beta
&& abs(beta) < VALUE_MATE_IN_MAX_PLY
&& abs(eval) < VALUE_KNOWN_WIN
&& eval < VALUE_KNOWN_WIN // Do not return unproven wins
&& pos.non_pawn_material(pos.side_to_move()))
return eval - futility_margin(depth);
@@ -607,7 +652,6 @@ namespace {
&& !ss->skipNullMove
&& depth >= 2 * ONE_PLY
&& eval >= beta
&& abs(beta) < VALUE_MATE_IN_MAX_PLY
&& pos.non_pawn_material(pos.side_to_move()))
{
ss->currentMove = MOVE_NULL;
@@ -615,9 +659,7 @@ namespace {
assert(eval - beta >= 0);
// Null move dynamic reduction based on depth and value
Depth R = 3 * ONE_PLY
+ depth / 4
+ int(eval - beta) / PawnValueMg * ONE_PLY;
Depth R = (3 + depth / 4 + std::min(int(eval - beta) / PawnValueMg, 3)) * ONE_PLY;
pos.do_null_move(st);
(ss+1)->skipNullMove = true;
@@ -632,7 +674,7 @@ namespace {
if (nullValue >= VALUE_MATE_IN_MAX_PLY)
nullValue = beta;
if (depth < 12 * ONE_PLY)
if (depth < 12 * ONE_PLY && abs(beta) < VALUE_KNOWN_WIN)
return nullValue;
// Do verification search at high depths
@@ -682,10 +724,9 @@ namespace {
&& !ttMove
&& (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;
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;
tte = TT.probe(posKey);
@@ -713,6 +754,8 @@ moves_loop: // When in check and at SpNode search starts from here
&& !SpNode
&& depth >= 8 * ONE_PLY
&& ttMove != MOVE_NONE
/* && ttValue != VALUE_NONE Already implicit in the next condition */
&& abs(ttValue) < VALUE_KNOWN_WIN
&& !excludedMove // Recursive singular search is not allowed
&& (tte->bound() & BOUND_LOWER)
&& 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);
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())
<< " currmovenumber " << moveCount + PVIdx << sync_endl;
}
@@ -777,12 +820,9 @@ moves_loop: // When in check and at SpNode search starts from here
if ( singularExtensionNode
&& move == ttMove
&& !ext
&& pos.legal(move, ci.pinned)
&& abs(ttValue) < VALUE_KNOWN_WIN)
&& pos.legal(move, ci.pinned))
{
assert(ttValue != VALUE_NONE);
Value rBeta = ttValue - int(depth);
Value rBeta = ttValue - int(2 * depth);
ss->excludedMove = move;
ss->skipNullMove = true;
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
if ( depth < 16 * ONE_PLY
&& moveCount >= FutilityMoveCounts[improving][depth] )
&& moveCount >= FutilityMoveCounts[improving][depth])
{
if (SpNode)
splitPoint->mutex.lock();
@@ -819,8 +859,8 @@ moves_loop: // When in check and at SpNode search starts from here
// Futility pruning: parent node
if (predictedDepth < 7 * ONE_PLY)
{
futilityValue = ss->staticEval + futility_margin(predictedDepth)
+ 128 + Gains[pos.moved_piece(move)][to_sq(move)];
futilityValue = ss->staticEval + futility_margin(predictedDepth)
+ 128 + Gains[pos.moved_piece(move)][to_sq(move)];
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
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);
if (!PvNode && cutNode)
if ( (!PvNode && cutNode)
|| History[pos.piece_on(to_sq(move))][to_sq(move)] < 0)
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])
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);
if (SpNode)
alpha = splitPoint->alpha;
@@ -906,20 +954,20 @@ moves_loop: // When in check and at SpNode search starts from here
if (SpNode)
alpha = splitPoint->alpha;
value = newDepth < ONE_PLY ?
givesCheck ? -qsearch<NonPV, true>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO)
: -qsearch<NonPV, false>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO)
: - search<NonPV, false>(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode);
value = newDepth < ONE_PLY ?
givesCheck ? -qsearch<NonPV, true>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO)
: -qsearch<NonPV, false>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO)
: - search<NonPV, 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
// 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.
if (PvNode && (pvMove || (value > alpha && (RootNode || value < beta))))
value = newDepth < ONE_PLY ?
givesCheck ? -qsearch<PV, true>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
: -qsearch<PV, false>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
: - search<PV, false>(pos, ss+1, -beta, -alpha, newDepth, false);
value = newDepth < ONE_PLY ?
givesCheck ? -qsearch<PV, true>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
: -qsearch<PV, false>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
: - search<PV, false>(pos, ss+1, -beta, -alpha, newDepth, false);
// Step 17. Undo 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);
thisThread->split<FakeSplit>(pos, ss, alpha, beta, &bestValue, &bestMove,
depth, moveCount, &mp, NT, cutNode);
thisThread->split(pos, ss, alpha, beta, &bestValue, &bestMove,
depth, moveCount, &mp, NT, cutNode);
if (Signals.stop || thisThread->cutoff_occurred())
return VALUE_ZERO;
@@ -1116,7 +1164,8 @@ moves_loop: // When in check and at SpNode search starts from here
bestValue = ttValue;
}
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
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)
continue;
// Speculative prefetch as early as possible
prefetch((char*)TT.first_entry(pos.key_after(move)));
// Check for legality just before making the move
if (!pos.legal(move, ci.pinned))
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
// 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);
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
// set using a statistical rule dependent on 'level'. Idea by Heinz van Saanen.
// When playing with a strength handicap, choose best move among the first 'candidates'
// RootMoves using a statistical rule dependent on 'level'. Idea by Heinz van Saanen.
Move Skill::pick_move() {
@@ -1313,7 +1365,7 @@ moves_loop: // When in check and at SpNode search starts from here
rk.rand<unsigned>();
// 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 max_s = -VALUE_INFINITE;
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
// weakness. One deterministic and bigger for weaker moves, and one random,
// 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;
// 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;
// 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;
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
ss << "\n";
ss << "info depth " << d
<< " 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()
<< " nps " << pos.nodes_searched() * 1000 / elapsed
<< " tbhits " << TBHits
<< " time " << elapsed
<< " multipv " << i + 1
<< " pv";
@@ -1463,46 +1525,13 @@ void Thread::idle_loop() {
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 (searching)
while (searching)
{
assert(!exit);
Threads.mutex.lock();
assert(searching);
assert(activeSplitPoint);
SplitPoint* sp = activeSplitPoint;
@@ -1559,7 +1588,7 @@ void Thread::idle_loop() {
if (Threads.size() > 2)
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;
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
// their work at this split point, return from the idle loop.
// 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())
{
this_sp->mutex.lock();
bool finished = this_sp->slavesMask.none(); // Retest under lock protection
this_sp->mutex.unlock();
if (finished)
return;
assert(!searching);
mutex.unlock();
break;
}
// 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];
Depth reduction;
Value staticEval;
int skipNullMove;
bool skipNullMove;
};
@@ -101,13 +101,12 @@ extern volatile SignalsType Signals;
extern LimitsType Limits;
extern std::vector<RootMove> RootMoves;
extern Position RootPos;
extern Color RootColor;
extern Time::point SearchTime;
extern StateStackPtr SetupStates;
extern void init();
extern uint64_t perft(Position& pos, Depth depth);
extern void think();
template<bool Root> uint64_t perft(Position& pos, Depth depth);
} // 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
// 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
// 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
// search() then split() returns.
template <bool Fake>
void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Value* bestValue,
Move* bestMove, Depth depth, int moveCount,
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;
activePosition = NULL;
if (!Fake)
for (Thread* slave; (slave = Threads.available_slave(this)) != NULL; )
{
sp.slavesMask.set(slave->idx);
slave->activeSplitPoint = &sp;
slave->searching = true; // Slave leaves idle_loop()
slave->notify_one(); // Could be sleeping
}
for (Thread* slave; (slave = Threads.available_slave(this)) != NULL; )
{
sp.slavesMask.set(slave->idx);
slave->activeSplitPoint = &sp;
slave->searching = true; // Slave leaves idle_loop()
slave->notify_one(); // Could be sleeping
}
// 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.
@@ -339,11 +337,6 @@ void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Valu
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
void ThreadPool::wait_for_think_finished() {

View File

@@ -117,7 +117,6 @@ struct Thread : public ThreadBase {
bool cutoff_occurred() 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,
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 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
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;
// Read uci parameters
int emergencyMoveHorizon = Options["Emergency Move Horizon"];
int emergencyBaseTime = Options["Emergency Base Time"];
int emergencyMoveTime = Options["Emergency Move Time"];
int minThinkingTime = Options["Minimum Thinking Time"];
int slowMover = Options["Slow Mover"];
int moveOverhead = Options["Move Overhead"];
int minThinkingTime = Options["Minimum Thinking Time"];
int slowMover = Options["Slow Mover"];
// Initialize unstablePvFactor to 1 and search times to maximum values
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
hypMyTime = limits.time[us]
+ limits.inc[us] * (hypMTG - 1)
- emergencyBaseTime
- emergencyMoveTime * std::min(hypMTG, emergencyMoveHorizon);
- moveOverhead * (2 + std::min(hypMTG, 40));
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,
/// 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 (hashMask == size - ClusterSize)
if (newClusterCount == clusterCount)
return;
hashMask = size - ClusterSize;
clusterCount = newClusterCount;
free(mem);
mem = calloc(size * sizeof(TTEntry) + CACHE_LINE_SIZE - 1, 1);
mem = calloc(clusterCount * sizeof(TTCluster) + CACHE_LINE_SIZE - 1, 1);
if (!mem)
{
@@ -50,7 +49,7 @@ void TranspositionTable::resize(uint64_t mbSize) {
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() {
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 {
TTEntry* tte = first_entry(key);
uint32_t key32 = key >> 32;
uint16_t key16 = key >> 48;
for (unsigned i = 0; i < ClusterSize; ++i, ++tte)
if (tte->key32 == key32)
for (unsigned i = 0; i < TTClusterSize; ++i, ++tte)
if (tte->key16 == key16)
{
tte->generation8 = generation; // Refresh
tte->genBound8 = generation | tte->bound(); // Refresh
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) {
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);
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)
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
if ( ( tte->generation8 == generation || tte->bound() == BOUND_EXACT)
- (replace->generation8 == generation)
- (tte->depth16 < replace->depth16) < 0)
if ( (( tte->genBound8 & 0xFC) == generation || tte->bound() == BOUND_EXACT)
- ((replace->genBound8 & 0xFC) == generation)
- (tte->depth8 < replace->depth8) < 0)
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 "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
/// bound type 8 bit
/// generation 8 bit
/// value 16 bit
/// depth 16 bit
/// eval value 16 bit
/// generation 6 bit
/// bound type 2 bit
/// depth 8 bit
struct TTEntry {
Move move() const { return (Move )move16; }
Bound bound() const { return (Bound)bound8; }
Value value() const { return (Value)value16; }
Depth depth() const { return (Depth)depth16; }
Value eval_value() const { return (Value)evalValue; }
Depth depth() const { return (Depth)depth8; }
Bound bound() const { return (Bound)(genBound8 & 0x3); }
private:
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;
move16 = (uint16_t)m;
bound8 = (uint8_t)b;
generation8 = (uint8_t)g;
value16 = (int16_t)v;
depth16 = (int16_t)d;
evalValue = (int16_t)ev;
key16 = (uint16_t)k;
move16 = (uint16_t)m;
value16 = (int16_t)v;
evalValue = (int16_t)ev;
genBound8 = (uint8_t)(g | b);
depth8 = (int8_t)d;
}
uint32_t key32;
uint16_t key16;
uint16_t move16;
uint8_t bound8, generation8;
int16_t value16, depth16, evalValue;
int16_t value16;
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
/// 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
/// not be bigger than a cache line size. In case it is less, it should be padded
/// to guarantee always aligned accesses.
class TranspositionTable {
static const unsigned ClusterSize = 4;
public:
~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;
TTEntry* first_entry(const Key key) const;
void resize(uint64_t mbSize);
void resize(size_t mbSize);
void clear();
void store(const Key key, Value v, Bound type, Depth d, Move m, Value statV);
private:
uint32_t hashMask;
TTEntry* table;
size_t clusterCount;
TTCluster* table;
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;
@@ -94,11 +104,11 @@ extern TranspositionTable TT;
/// 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
/// get the index of the cluster.
/// get the index of the cluster inside the table.
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

View File

@@ -136,7 +136,7 @@ enum CastlingSide {
KING_SIDE, QUEEN_SIDE, CASTLING_SIDE_NB = 2
};
enum CastlingRight { // Defined as in PolyGlot book hash key
enum CastlingRight {
NO_CASTLING,
WHITE_OO,
WHITE_OOO = WHITE_OO << 1,
@@ -181,8 +181,8 @@ enum Value {
VALUE_INFINITE = 32001,
VALUE_NONE = 32002,
VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY,
VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + MAX_PLY,
VALUE_MATE_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY,
VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + 2 * MAX_PLY,
VALUE_ENSURE_INTEGER_SIZE_P = INT_MAX,
VALUE_ENSURE_INTEGER_SIZE_N = INT_MIN,
@@ -211,14 +211,14 @@ enum Piece {
enum Depth {
ONE_PLY = 2,
ONE_PLY = 1,
DEPTH_ZERO = 0 * ONE_PLY,
DEPTH_QS_CHECKS = 0 * ONE_PLY,
DEPTH_QS_NO_CHECKS = -1 * ONE_PLY,
DEPTH_QS_RECAPTURES = -5 * ONE_PLY,
DEPTH_ZERO = 0,
DEPTH_QS_CHECKS = 0,
DEPTH_QS_NO_CHECKS = -1,
DEPTH_QS_RECAPTURES = -5,
DEPTH_NONE = -127 * ONE_PLY
DEPTH_NONE = -6
};
enum Square {
@@ -267,28 +267,17 @@ enum Score {
SCORE_ENSURE_INTEGER_SIZE_N = INT_MIN
};
typedef union {
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);
}
inline Score make_score(int mg, int eg) { return Score((mg << 16) + eg); }
/// 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) {
ScoreView v;
v.full = s;
return Value(v.half.mg + (uint16_t(v.half.eg) >> 15));
return Value(((s + 0x8000) & ~0xffff) / 0x10000);
}
inline Value eg_value(Score s) {
ScoreView v;
v.full = s;
return Value(v.half.eg);
return Value((int)(unsigned(s) & 0x7FFFU) - (int)(unsigned(s) & 0x8000U));
}
#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);
}
CACHE_LINE_ALIGNMENT
extern Value PieceValue[PHASE_NB][PIECE_NB];
struct ExtMove {
@@ -414,14 +405,6 @@ inline bool opposite_colors(Square s1, Square s2) {
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) {
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
}
#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

View File

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

View File

@@ -27,6 +27,7 @@
#include "thread.h"
#include "tt.h"
#include "ucioption.h"
#include "tbprobe.h"
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_hash_size(const Option& o) { TT.resize(o); }
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
@@ -54,35 +56,23 @@ bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const
void init(OptionsMap& o) {
o["Write Debug Log"] << Option(false, on_logger);
o["Write Search Log"] << Option(false);
o["Search Log Filename"] << Option("SearchLog.txt");
o["Book File"] << Option("book.bin");
o["Best Book Move"] << Option(false);
o["Contempt Factor"] << Option(0, -50, 50);
o["Mobility (Midgame)"] << Option(100, 0, 200, on_eval);
o["Mobility (Endgame)"] << Option(100, 0, 200, on_eval);
o["Pawn Structure (Midgame)"] << Option(100, 0, 200, on_eval);
o["Pawn Structure (Endgame)"] << Option(100, 0, 200, on_eval);
o["Passed Pawns (Midgame)"] << Option(100, 0, 200, on_eval);
o["Passed Pawns (Endgame)"] << Option(100, 0, 200, on_eval);
o["Space"] << Option(100, 0, 200, on_eval);
o["Aggressiveness"] << Option(100, 0, 200, on_eval);
o["Cowardice"] << Option(100, 0, 200, on_eval);
o["Min Split Depth"] << Option(0, 0, 12, on_threads);
o["Threads"] << Option(1, 1, MAX_THREADS, on_threads);
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);
o["Write Debug Log"] << Option(false, on_logger);
o["Contempt"] << Option(0, -100, 100);
o["Min Split Depth"] << Option(0, 0, 12, on_threads);
o["Threads"] << Option(1, 1, MAX_THREADS, on_threads);
o["Hash"] << Option(16, 1, 1024 * 1024, on_hash_size);
o["Clear Hash"] << Option(on_clear_hash);
o["Ponder"] << Option(true);
o["MultiPV"] << Option(1, 1, 500);
o["Skill Level"] << Option(20, 0, 20);
o["Move Overhead"] << Option(30, 0, 5000);
o["Minimum Thinking Time"] << Option(20, 0, 5000);
o["Slow Mover"] << Option(80, 10, 1000);
o["UCI_Chess960"] << Option(false);
o["SyzygyPath"] << Option("<empty>", on_tb_path);
o["SyzygyProbeDepth"] << Option(1, 1, 100);
o["Syzygy50MoveRule"] << Option(true);
o["SyzygyProbeLimit"] << Option(6, 0, 6);
}