mirror of
https://github.com/peterosterlund2/droidfish.git
synced 2025-12-17 19:22:18 +01:00
DroidFish: Updated stockfish to the latest development version and included syzygy tablebases probing code.
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ namespace {
|
||||
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)));
|
||||
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
|
||||
|
||||
@@ -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 },
|
||||
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));
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#ifndef BITBOARD_H_INCLUDED
|
||||
#define BITBOARD_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace Bitboards {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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,15 +740,15 @@ 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))
|
||||
{
|
||||
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
|
||||
@@ -777,6 +763,12 @@ namespace {
|
||||
// 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)]);
|
||||
}
|
||||
}
|
||||
|
||||
// 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)) << " "
|
||||
@@ -848,10 +840,10 @@ namespace {
|
||||
ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2)
|
||||
<< " Eval term | White | Black | Total \n"
|
||||
<< " | MG EG | MG EG | MG EG \n"
|
||||
<< "---------------------+-------------+-------------+-------------\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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -30,9 +30,9 @@ namespace {
|
||||
// Polynomial material balance parameters
|
||||
|
||||
// pair pawn knight bishop rook queen
|
||||
const int LinearCoefficients[6] = { 1852, -162, -1122, -183, 249, -154 };
|
||||
const int Linear[6] = { 1852, -162, -1122, -183, 249, -154 };
|
||||
|
||||
const int QuadraticCoefficientsSameSide[][PIECE_TYPE_NB] = {
|
||||
const int QuadraticSameSide[][PIECE_TYPE_NB] = {
|
||||
// OUR PIECES
|
||||
// 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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(); }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 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];
|
||||
+ StormDanger[rkUs == RANK_1 ? 0 :
|
||||
rkThem != rkUs + 1 ? 1 : 2][rkThem];
|
||||
}
|
||||
|
||||
return safety;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
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(pos, depth - ONE_PLY);
|
||||
cnt = leaf ? MoveList<LEGAL>(pos).size() : perft<false>(pos, depth - ONE_PLY);
|
||||
nodes += cnt;
|
||||
pos.undo_move(*it);
|
||||
}
|
||||
return cnt;
|
||||
if (Root)
|
||||
sync_cout << move_to_uci(*it, pos.is_chess960()) << ": " << cnt << sync_endl;
|
||||
}
|
||||
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 (piecesCnt <= TBCardinality)
|
||||
{
|
||||
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 (Options["Write Search Log"])
|
||||
if (!RootInTB)
|
||||
{
|
||||
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 = 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);
|
||||
@@ -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;
|
||||
@@ -994,7 +1042,7 @@ 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,
|
||||
thisThread->split(pos, ss, alpha, beta, &bestValue, &bestMove,
|
||||
depth, moveCount, &mp, NT, cutNode);
|
||||
|
||||
if (Signals.stop || thisThread->cutoff_occurred())
|
||||
@@ -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,7 +1373,7 @@ 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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
1620
DroidFish/jni/stockfish/tbcore.cpp
Normal file
1620
DroidFish/jni/stockfish/tbcore.cpp
Normal file
File diff suppressed because it is too large
Load Diff
142
DroidFish/jni/stockfish/tbcore.h
Normal file
142
DroidFish/jni/stockfish/tbcore.h
Normal 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
|
||||
819
DroidFish/jni/stockfish/tbprobe.cpp
Normal file
819
DroidFish/jni/stockfish/tbprobe.cpp
Normal 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;
|
||||
}
|
||||
|
||||
16
DroidFish/jni/stockfish/tbprobe.h
Normal file
16
DroidFish/jni/stockfish/tbprobe.h
Normal 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
|
||||
@@ -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,7 +296,6 @@ 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);
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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,9 +89,7 @@ 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 moveOverhead = Options["Move Overhead"];
|
||||
int minThinkingTime = Options["Minimum Thinking Time"];
|
||||
int slowMover = Options["Slow Mover"];
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
key16 = (uint16_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;
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -55,34 +57,22 @@ 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["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(32, 1, 16384, on_hash_size);
|
||||
o["Hash"] << Option(16, 1, 1024 * 1024, 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["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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user