diff --git a/DroidFish/jni/stockfish/Android.mk b/DroidFish/jni/stockfish/Android.mk index 3b5a175..c7b8595 100644 --- a/DroidFish/jni/stockfish/Android.mk +++ b/DroidFish/jni/stockfish/Android.mk @@ -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) diff --git a/DroidFish/jni/stockfish/benchmark.cpp b/DroidFish/jni/stockfish/benchmark.cpp index 2f943c4..3161c4c 100644 --- a/DroidFish/jni/stockfish/benchmark.cpp +++ b/DroidFish/jni/stockfish/benchmark.cpp @@ -17,6 +17,7 @@ along with this program. If not, see . */ +#include #include #include #include @@ -81,7 +82,7 @@ void benchmark(const Position& current, istream& is) { vector 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 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(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 diff --git a/DroidFish/jni/stockfish/bitbase.cpp b/DroidFish/jni/stockfish/bitbase.cpp index 6094f50..0f274c8 100644 --- a/DroidFish/jni/stockfish/bitbase.cpp +++ b/DroidFish/jni/stockfish/bitbase.cpp @@ -107,11 +107,11 @@ namespace { KPKPosition::KPKPosition(unsigned idx) { - wksq = Square((idx >> 0) & 0x3F); - bksq = Square((idx >> 6) & 0x3F); - us = Color ((idx >> 12) & 0x01); - psq = make_square(File((idx >> 13) & 0x03), Rank(RANK_7 - (idx >> 15))); - result = UNKNOWN; + wksq = Square((idx >> 0) & 0x3F); + bksq = Square((idx >> 6) & 0x3F); + us = Color ((idx >> 12) & 0x01); + psq = make_square(File((idx >> 13) & 0x3), RANK_7 - Rank((idx >> 15) & 0x7)); + result = UNKNOWN; // Check if two pieces are on the same square or if a king can be captured if ( square_distance(wksq, bksq) <= 1 diff --git a/DroidFish/jni/stockfish/bitboard.cpp b/DroidFish/jni/stockfish/bitboard.cpp index d4b433f..b3dbd00 100644 --- a/DroidFish/jni/stockfish/bitboard.cpp +++ b/DroidFish/jni/stockfish/bitboard.cpp @@ -149,7 +149,10 @@ const std::string Bitboards::pretty(Bitboard b) { void Bitboards::init() { for (Square s = SQ_A1; s <= SQ_H8; ++s) - BSFTable[bsf_index(SquareBB[s] = 1ULL << s)] = s; + { + SquareBB[s] = 1ULL << s; + BSFTable[bsf_index(SquareBB[s])] = s; + } for (Bitboard b = 1; b < 256; ++b) MS1BTable[b] = more_than_one(b) ? MS1BTable[b - 1] : lsb(b); @@ -251,9 +254,8 @@ namespace { void init_magics(Bitboard table[], Bitboard* attacks[], Bitboard magics[], Bitboard masks[], unsigned shifts[], Square deltas[], Fn index) { - int MagicBoosters[][8] = { { 969, 1976, 2850, 542, 2069, 2852, 1708, 164 }, - { 3101, 552, 3555, 926, 834, 26, 2131, 1117 } }; - + int MagicBoosters[][RANK_NB] = { { 969, 1976, 2850, 542, 2069, 2852, 1708, 164 }, + { 3101, 552, 3555, 926, 834, 26, 2131, 1117 } }; RKISS rk; Bitboard occupancy[4096], reference[4096], edges, b; int i, size, booster; @@ -301,7 +303,8 @@ namespace { // Find a magic for square 's' picking up an (almost) random number // until we find the one that passes the verification test. do { - do magics[s] = rk.magic_rand(booster); + do + magics[s] = rk.magic_rand(booster); while (popcount((magics[s] * masks[s]) >> 56) < 6); std::memset(attacks[s], 0, size * sizeof(Bitboard)); diff --git a/DroidFish/jni/stockfish/bitboard.h b/DroidFish/jni/stockfish/bitboard.h index 9814268..6a1755b 100644 --- a/DroidFish/jni/stockfish/bitboard.h +++ b/DroidFish/jni/stockfish/bitboard.h @@ -21,6 +21,8 @@ #ifndef BITBOARD_H_INCLUDED #define BITBOARD_H_INCLUDED +#include + #include "types.h" namespace Bitboards { diff --git a/DroidFish/jni/stockfish/bitcount.h b/DroidFish/jni/stockfish/bitcount.h index f84c51c..9feed19 100644 --- a/DroidFish/jni/stockfish/bitcount.h +++ b/DroidFish/jni/stockfish/bitcount.h @@ -96,8 +96,7 @@ inline int popcount(Bitboard b) { #else - __asm__("popcnt %1, %0" : "=r" (b) : "r" (b)); - return b; + return __builtin_popcountll(b); #endif } diff --git a/DroidFish/jni/stockfish/book.cpp b/DroidFish/jni/stockfish/book.cpp deleted file mode 100644 index fbbc3fe..0000000 --- a/DroidFish/jni/stockfish/book.cpp +++ /dev/null @@ -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 . -*/ - -/* - 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 -#include - -#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 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() % 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(from_sq(move), to_sq(move), PieceType(pt + 1)); - - // Add 'special move' flags and verify it is legal - for (MoveList 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; -} diff --git a/DroidFish/jni/stockfish/book.h b/DroidFish/jni/stockfish/book.h deleted file mode 100644 index fc84e09..0000000 --- a/DroidFish/jni/stockfish/book.h +++ /dev/null @@ -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 . -*/ - -#ifndef BOOK_H_INCLUDED -#define BOOK_H_INCLUDED - -#include -#include - -#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 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 diff --git a/DroidFish/jni/stockfish/evaluate.cpp b/DroidFish/jni/stockfish/evaluate.cpp index 64b1bf4..69df762 100644 --- a/DroidFish/jni/stockfish/evaluate.cpp +++ b/DroidFish/jni/stockfish/evaluate.cpp @@ -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(Us) && pos.non_pawn_material(Us) > QueenValueMg + PawnValueMg) + if (pos.non_pawn_material(Us) > QueenValueMg + PawnValueMg) { ei.kingRing[Them] = b | shift_bb(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 - 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(pos, ei, s); + score += evaluate_outpost(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(undefended)) + 2 * (ei.pinnedPieces[Us] != 0) - - mg_value(score) / 32; + - mg_value(score) / 32 + - !pos.count(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(b) - * (Them == pos.side_to_move() ? 2 : 1); + attackUnits += QueenContactCheck * popcount(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(b) - * (Them == pos.side_to_move() ? 2 : 1); + attackUnits += RookContactCheck * popcount(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(b) - : Hanging[Us == pos.side_to_move()]; + score += more_than_one(b) ? Hanging * popcount(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(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(s); - if ( unlikely(forward_bb(Them, s) & pos.pieces(Us, ROOK, QUEEN)) - && (forward_bb(Them, s) & pos.pieces(Us, ROOK, QUEEN) & pos.attacks_from(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(Us) < pos.count(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(pos, ei) - evaluate_passed_pawns(pos, ei); - // If one side has only a king, score for potential unstoppable pawns - if (!pos.non_pawn_material(WHITE) || !pos.non_pawn_material(BLACK)) - score += evaluate_unstoppable_pawns(pos, WHITE, ei) - - evaluate_unstoppable_pawns(pos, BLACK, ei); + // If both sides have only pawns, score for potential unstoppable pawns + if (!pos.non_pawn_material(WHITE) && !pos.non_pawn_material(BLACK)) + score += evaluate_unstoppable_pawns(WHITE, ei) + - evaluate_unstoppable_pawns(BLACK, ei); // Evaluate space for both sides, only in middlegame if (ei.mi->space_weight()) @@ -754,29 +740,35 @@ namespace { } // Scale winning side if position is more drawish than it appears - ScaleFactor sf = eg_value(score) > VALUE_DRAW ? ei.mi->scale_factor(pos, WHITE) - : ei.mi->scale_factor(pos, BLACK); + Color strongSide = eg_value(score) > VALUE_DRAW ? WHITE : BLACK; + ScaleFactor sf = ei.mi->scale_factor(pos, strongSide); - // If we don't already have an unusual scale factor, check for opposite - // colored bishop endgames, and use a lower scale for those. + // If we don't already have an unusual scale factor, check for certain + // types of endgames, and use a lower scale for those. if ( ei.mi->game_phase() < PHASE_MIDGAME - && pos.opposite_bishops() && (sf == SCALE_FACTOR_NORMAL || sf == SCALE_FACTOR_ONEPAWN)) { - // Ignoring any pawns, do both sides only have a single bishop and no - // other pieces? - if ( pos.non_pawn_material(WHITE) == BishopValueMg - && pos.non_pawn_material(BLACK) == BishopValueMg) - { - // Check for KBP vs KB with only a single pawn that is almost - // certainly a draw or at least two pawns. - bool one_pawn = (pos.count(WHITE) + pos.count(BLACK) == 1); - sf = one_pawn ? ScaleFactor(8) : ScaleFactor(32); + if (pos.opposite_bishops()) { + // Ignoring any pawns, do both sides only have a single bishop and no + // other pieces? + if ( pos.non_pawn_material(WHITE) == BishopValueMg + && pos.non_pawn_material(BLACK) == BishopValueMg) + { + // Check for KBP vs KB with only a single pawn that is almost + // certainly a draw or at least two pawns. + bool one_pawn = (pos.count(WHITE) + pos.count(BLACK) == 1); + sf = one_pawn ? ScaleFactor(8) : ScaleFactor(32); + } + else + // Endgame with opposite-colored bishops, but also other pieces. Still + // a bit drawish, but not as drawish as with only the two bishops. + sf = ScaleFactor(50 * sf / SCALE_FACTOR_NORMAL); + } else if ( abs(eg_value(score)) <= BishopValueEg + && ei.pi->pawn_span(strongSide) <= 1 + && !pos.pawn_passed(~strongSide, pos.king_square(~strongSide))) { + // Endings where weaker side can be place his king in front of the opponent's pawns are drawish. + sf = ScaleFactor(ScalePawnSpan[ei.pi->pawn_span(strongSide)]); } - else - // Endgame with opposite-colored bishops, but also other pieces. Still - // a bit drawish, but not as drawish as with only the two bishops. - sf = ScaleFactor(50 * sf / SCALE_FACTOR_NORMAL); } // Interpolate between a middlegame and a (scaled by 'sf') endgame score @@ -788,7 +780,7 @@ namespace { // In case of tracing add all single evaluation contributions for both white and black if (Trace) { - Tracing::add_term(Tracing::PST, pos.psq_score()); + Tracing::add_term(Tracing::MATERIAL, pos.psq_score()); Tracing::add_term(Tracing::IMBALANCE, ei.mi->material_value()); Tracing::add_term(PAWN, ei.pi->pawns_value()); Tracing::add_term(Tracing::MOBILITY, apply_weight(mobility[WHITE], Weights[Mobility]) @@ -821,13 +813,13 @@ namespace { Score bScore = terms[BLACK][idx]; switch (idx) { - case PST: case IMBALANCE: case PAWN: case TOTAL: - ss << std::setw(20) << name << " | --- --- | --- --- | " + case MATERIAL: case IMBALANCE: case PAWN: case TOTAL: + ss << std::setw(15) << name << " | --- --- | --- --- | " << std::setw(5) << to_cp(mg_value(wScore - bScore)) << " " << std::setw(5) << to_cp(eg_value(wScore - bScore)) << " \n"; break; default: - ss << std::setw(20) << name << " | " << std::noshowpos + ss << std::setw(15) << name << " | " << std::noshowpos << std::setw(5) << to_cp(mg_value(wScore)) << " " << std::setw(5) << to_cp(eg_value(wScore)) << " | " << std::setw(5) << to_cp(mg_value(bScore)) << " " @@ -846,12 +838,12 @@ namespace { std::stringstream ss; ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2) - << " Eval term | White | Black | Total \n" - << " | MG EG | MG EG | MG EG \n" - << "---------------------+-------------+-------------+-------------\n"; + << " Eval term | White | Black | Total \n" + << " | MG EG | MG EG | MG EG \n" + << "----------------+-------------+-------------+-------------\n"; - format_row(ss, "Material, PST, Tempo", PST); - format_row(ss, "Material imbalance", IMBALANCE); + format_row(ss, "Material", MATERIAL); + format_row(ss, "Imbalance", IMBALANCE); format_row(ss, "Pawns", PAWN); format_row(ss, "Knights", KNIGHT); format_row(ss, "Bishops", BISHOP); @@ -863,7 +855,7 @@ namespace { format_row(ss, "Passed pawns", PASSED); format_row(ss, "Space", SPACE); - ss << "---------------------+-------------+-------------+-------------\n"; + ss << "----------------+-------------+-------------+-------------\n"; format_row(ss, "Total", TOTAL); ss << "\nTotal Evaluation: " << to_cp(v) << " (white side)\n"; @@ -880,7 +872,7 @@ namespace Eval { /// of the position always from the point of view of the side to move. Value evaluate(const Position& pos) { - return do_evaluate(pos); + return do_evaluate(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]); } } diff --git a/DroidFish/jni/stockfish/evaluate.h b/DroidFish/jni/stockfish/evaluate.h index 73df76a..fff2476 100644 --- a/DroidFish/jni/stockfish/evaluate.h +++ b/DroidFish/jni/stockfish/evaluate.h @@ -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); diff --git a/DroidFish/jni/stockfish/main.cpp b/DroidFish/jni/stockfish/main.cpp index cbb260f..69d29c2 100644 --- a/DroidFish/jni/stockfish/main.cpp +++ b/DroidFish/jni/stockfish/main.cpp @@ -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); diff --git a/DroidFish/jni/stockfish/material.cpp b/DroidFish/jni/stockfish/material.cpp index 9a12066..b7db134 100644 --- a/DroidFish/jni/stockfish/material.cpp +++ b/DroidFish/jni/stockfish/material.cpp @@ -29,10 +29,10 @@ namespace { // Polynomial material balance parameters - // pair pawn knight bishop rook queen - const int LinearCoefficients[6] = { 1852, -162, -1122, -183, 249, -154 }; + // pair pawn knight bishop rook queen + const int Linear[6] = { 1852, -162, -1122, -183, 249, -154 }; - const int QuadraticCoefficientsSameSide[][PIECE_TYPE_NB] = { + const int QuadraticSameSide[][PIECE_TYPE_NB] = { // OUR PIECES // pair pawn knight bishop rook queen { 0 }, // Bishop pair @@ -43,7 +43,7 @@ namespace { {-177, 25, 129, 142, -137, 0 } // Queen }; - const int QuadraticCoefficientsOppositeSide[][PIECE_TYPE_NB] = { + const int QuadraticOppositeSide[][PIECE_TYPE_NB] = { // THEIR PIECES // pair pawn knight bishop rook queen { 0 }, // Bishop pair @@ -66,8 +66,7 @@ namespace { // Helper templates used to detect a given material distribution template bool is_KXK(const Position& pos) { const Color Them = (Us == WHITE ? BLACK : WHITE); - return !pos.count(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 diff --git a/DroidFish/jni/stockfish/material.h b/DroidFish/jni/stockfish/material.h index 9116361..f05541c 100644 --- a/DroidFish/jni/stockfish/material.h +++ b/DroidFish/jni/stockfish/material.h @@ -68,7 +68,6 @@ struct Entry { typedef HashTable Table; Entry* probe(const Position& pos, Table& entries, Endgames& endgames); -Phase game_phase(const Position& pos); } // namespace Material diff --git a/DroidFish/jni/stockfish/misc.cpp b/DroidFish/jni/stockfish/misc.cpp index ebbb778..3519611 100644 --- a/DroidFish/jni/stockfish/misc.cpp +++ b/DroidFish/jni/stockfish/misc.cpp @@ -17,6 +17,7 @@ along with this program. If not, see . */ +#include #include #include #include @@ -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 diff --git a/DroidFish/jni/stockfish/misc.h b/DroidFish/jni/stockfish/misc.h index 92bde8f..970f335 100644 --- a/DroidFish/jni/stockfish/misc.h +++ b/DroidFish/jni/stockfish/misc.h @@ -20,7 +20,7 @@ #ifndef MISC_H_INCLUDED #define MISC_H_INCLUDED -#include +#include #include #include @@ -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(); } diff --git a/DroidFish/jni/stockfish/notation.cpp b/DroidFish/jni/stockfish/notation.cpp index cace78b..7e8765f 100644 --- a/DroidFish/jni/stockfish/notation.cpp +++ b/DroidFish/jni/stockfish/notation.cpp @@ -18,9 +18,7 @@ */ #include -#include #include -#include #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(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(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 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; -} diff --git a/DroidFish/jni/stockfish/notation.h b/DroidFish/jni/stockfish/notation.h index 730e841..251a0ca 100644 --- a/DroidFish/jni/stockfish/notation.h +++ b/DroidFish/jni/stockfish/notation.h @@ -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 diff --git a/DroidFish/jni/stockfish/pawns.cpp b/DroidFish/jni/stockfish/pawns.cpp index 5768eb3..4693e15 100644 --- a/DroidFish/jni/stockfish/pawns.cpp +++ b/DroidFish/jni/stockfish/pawns.cpp @@ -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(Us); + const Bitboard* pawnAttacksBB = StepAttacksBB[make_piece(Us, PAWN)]; - Bitboard ourPawns = pos.pieces(Us, PAWN); + Bitboard ourPawns = pos.pieces(Us , PAWN); Bitboard theirPawns = pos.pieces(Them, PAWN); - e->passedPawns[Us] = e->candidatePawns[Us] = 0; + e->passedPawns[Us] = 0; e->kingSquares[Us] = SQ_NONE; e->semiopenFiles[Us] = 0xFF; e->pawnAttacks[Us] = shift_bb(ourPawns) | shift_bb(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(b) >= popcount(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(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 Value Entry::shelter_storm(const Position& pos, Square ksq) { const Color Them = (Us == WHITE ? BLACK : WHITE); - static const Bitboard MiddleEdges = (FileABB | FileHBB) & (Rank2BB | Rank3BB); + const Bitboard Edges = (FileABB | FileHBB) & (Rank2BB | Rank3BB); - Value safety = MaxSafetyBonus; Bitboard b = pos.pieces(PAWN) & (in_front_bb(Us, rank_of(ksq)) | rank_bb(ksq)); Bitboard ourPawns = b & pos.pieces(Us); Bitboard theirPawns = b & pos.pieces(Them); - Rank rkUs, rkThem; + Value safety = MaxSafetyBonus; File kf = std::max(FILE_B, std::min(FILE_G, file_of(ksq))); for (File f = kf - File(1); f <= kf + File(1); ++f) { b = ourPawns & file_bb(f); - rkUs = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1; + Rank rkUs = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1; b = theirPawns & file_bb(f); - rkThem = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1; + Rank rkThem = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1; - if ( (MiddleEdges & make_square(f, rkThem)) + if ( (Edges & make_square(f, rkThem)) && file_of(ksq) == f && relative_rank(Us, ksq) == rkThem - 1) safety += 200; else - safety -= ShelterWeakness[rkUs] - + StormDanger[rkUs == RANK_1 ? 0 : rkThem == rkUs + 1 ? 2 : 1][rkThem]; + safety -= ShelterWeakness[rkUs] + + StormDanger[rkUs == RANK_1 ? 0 : + rkThem != rkUs + 1 ? 1 : 2][rkThem]; } return safety; diff --git a/DroidFish/jni/stockfish/pawns.h b/DroidFish/jni/stockfish/pawns.h index 28fe1c0..f24fb75 100644 --- a/DroidFish/jni/stockfish/pawns.h +++ b/DroidFish/jni/stockfish/pawns.h @@ -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 Table; -void init(); Entry* probe(const Position& pos, Table& entries); } // namespace Pawns diff --git a/DroidFish/jni/stockfish/position.cpp b/DroidFish/jni/stockfish/position.cpp index 982352c..cbaa07e 100644 --- a/DroidFish/jni/stockfish/position.cpp +++ b/DroidFish/jni/stockfish/position.cpp @@ -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(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 it(*this); *it; ++it) - ss << move_to_san(*const_cast(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(*this).size())) return true; diff --git a/DroidFish/jni/stockfish/position.h b/DroidFish/jni/stockfish/position.h index 0f88944..7f7d2c5 100644 --- a/DroidFish/jni/stockfish/position.h +++ b/DroidFish/jni/stockfish/position.h @@ -24,9 +24,9 @@ #include #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 int count(Color c) const; template 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(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 diff --git a/DroidFish/jni/stockfish/search.cpp b/DroidFish/jni/stockfish/search.cpp index 4ac4c08..64233d2 100644 --- a/DroidFish/jni/stockfish/search.cpp +++ b/DroidFish/jni/stockfish/search.cpp @@ -19,18 +19,18 @@ #include #include -#include #include #include #include #include -#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 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 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 +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 it(pos); *it; ++it) { - pos.do_move(*it, st, ci, pos.gives_check(*it, ci)); - cnt += leaf ? MoveList(pos).size() : ::perft(pos, depth - ONE_PLY); - pos.undo_move(*it); + if (Root && depth <= ONE_PLY) + cnt = 1, nodes++; + else + { + pos.do_move(*it, st, ci, pos.gives_check(*it, ci)); + cnt = leaf ? MoveList(pos).size() : perft(pos, depth - ONE_PLY); + nodes += cnt; + pos.undo_move(*it); + } + if (Root) + sync_cout << move_to_uci(*it, pos.is_chess960()) << ": " << cnt << sync_endl; } - return cnt; + return nodes; } -uint64_t Search::perft(Position& pos, Depth depth) { - return depth > ONE_PLY ? ::perft(pos, depth) : MoveList(pos).size(); -} +template uint64_t Search::perft(Position& pos, Depth depth); + /// Search::think() is the external interface to Stockfish's search, and is /// called by the main thread when the program receives the UCI 'go' command. It @@ -180,14 +189,14 @@ uint64_t Search::perft(Position& pos, Depth depth) { void Search::think() { - static PolyglotBook book; // Defined static to initialize the PRNG only once + TimeMgr.init(Limits, RootPos.game_ply(), RootPos.side_to_move()); + int piecesCnt; + TBHits = TBCardinality = 0; + RootInTB = false; - RootColor = RootPos.side_to_move(); - TimeMgr.init(Limits, RootPos.game_ply(), RootColor); - - int cf = Options["Contempt Factor"] * PawnValueEg / 100; // From centipawns - DrawValue[ RootColor] = VALUE_DRAW - Value(cf); - DrawValue[~RootColor] = VALUE_DRAW + Value(cf); + int cf = Options["Contempt"] * PawnValueEg / 100; // From centipawns + DrawValue[ RootPos.side_to_move()] = VALUE_DRAW - Value(cf); + DrawValue[~RootPos.side_to_move()] = VALUE_DRAW + Value(cf); if (RootMoves.empty()) { @@ -199,27 +208,55 @@ void Search::think() { goto finalize; } - if (Options["OwnBook"] && !Limits.infinite && !Limits.mate) + piecesCnt = RootPos.total_piece_count(); + TBCardinality = Options["SyzygyProbeLimit"]; + TBProbeDepth = Options["SyzygyProbeDepth"] * ONE_PLY; + if (TBCardinality > Tablebases::TBLargest) { - Move bookMove = book.probe(RootPos, Options["Book File"], Options["Best Book Move"]); - - if (bookMove && std::count(RootMoves.begin(), RootMoves.end(), bookMove)) - { - std::swap(RootMoves[0], *std::find(RootMoves.begin(), RootMoves.end(), bookMove)); - goto finalize; - } + TBCardinality = Tablebases::TBLargest; + TBProbeDepth = 0 * ONE_PLY; } + TB50MoveRule = Options["Syzygy50MoveRule"]; - if (Options["Write Search Log"]) + if (piecesCnt <= TBCardinality) { - Log log(Options["Search Log Filename"]); - log << "\nSearching: " << RootPos.fen() - << "\ninfinite: " << Limits.infinite - << " ponder: " << Limits.ponder - << " time: " << Limits.time[RootColor] - << " increment: " << Limits.inc[RootColor] - << " moves to go: " << Limits.movestogo - << "\n" << std::endl; + TBHits = RootMoves.size(); + + // If the current root position is in the tablebases then RootMoves + // contains only moves that preserve the draw or win. + RootInTB = Tablebases::root_probe(RootPos, TBScore); + + if (RootInTB) + { + TBCardinality = 0; // Do not probe tablebases during the search + + // It might be a good idea to mangle the hash key (xor it + // with a fixed value) in order to "clear" the hash table of + // the results of previous probes. However, that would have to + // be done from within the Position class, so we skip it for now. + + // Optional: decrease target time. + } + else // If DTZ tables are missing, use WDL tables as a fallback + { + // Filter out moves that do not preserve a draw or win + RootInTB = Tablebases::root_probe_wdl(RootPos, TBScore); + + // Only probe during search if winning + if (TBScore <= VALUE_DRAW) + TBCardinality = 0; + } + + if (!RootInTB) + { + TBHits = 0; + } + else if (!TB50MoveRule) + { + TBScore = TBScore > VALUE_DRAW ? VALUE_MATE - MAX_PLY - 1 + : TBScore < VALUE_DRAW ? -VALUE_MATE + MAX_PLY + 1 + : TBScore; + } } // Reset the threads, still sleeping: will wake up at split time @@ -233,25 +270,16 @@ void Search::think() { Threads.timer->run = false; // Stop the timer - if (Options["Write Search Log"]) + if (RootInTB) { - Time::point elapsed = Time::now() - SearchTime + 1; - - Log log(Options["Search Log Filename"]); - log << "Nodes: " << RootPos.nodes_searched() - << "\nNodes/second: " << RootPos.nodes_searched() * 1000 / elapsed - << "\nBest move: " << move_to_san(RootPos, RootMoves[0].pv[0]); - - StateInfo st; - RootPos.do_move(RootMoves[0].pv[0], st); - log << "\nPonder move: " << move_to_san(RootPos, RootMoves[0].pv[1]) << std::endl; - RootPos.undo_move(RootMoves[0].pv[0]); + // If we mangled the hash key, unmangle it here } finalize: // When search is stopped this info is not printed sync_cout << "info nodes " << RootPos.nodes_searched() + << " tbhits " << TBHits << " time " << Time::now() - SearchTime + 1 << sync_endl; // When we reach the maximum depth, we can arrive here without a raise of @@ -285,7 +313,6 @@ namespace { Value bestValue, alpha, beta, delta; std::memset(ss-2, 0, 5 * sizeof(Stack)); - (ss-1)->currentMove = MOVE_NULL; // Hack to skip update gains depth = 0; BestMoveChanges = 0; @@ -298,15 +325,12 @@ namespace { Countermoves.clear(); Followupmoves.clear(); - MultiPV = Options["MultiPV"]; - Skill skill(Options["Skill Level"]); + size_t multiPV = Options["MultiPV"]; + Skill skill(Options["Skill Level"], RootMoves.size()); // Do we have to play with skill handicap? In this case enable MultiPV search // that we will use behind the scenes to retrieve a set of possible moves. - if (skill.enabled() && MultiPV < 4) - MultiPV = 4; - - MultiPV = std::min(MultiPV, RootMoves.size()); + multiPV = std::max(multiPV, skill.candidates_size()); // Iterative deepening loop until requested to stop or target depth reached while (++depth <= MAX_PLY && !Signals.stop && (!Limits.depth || depth <= Limits.depth)) @@ -320,7 +344,7 @@ namespace { RootMoves[i].prevScore = RootMoves[i].score; // MultiPV loop. We perform a full root search for each PV line - for (PVIdx = 0; PVIdx < MultiPV && !Signals.stop; ++PVIdx) + for (PVIdx = 0; PVIdx < std::min(multiPV, RootMoves.size()) && !Signals.stop; ++PVIdx) { // Reset aspiration window starting size if (depth >= 5) @@ -377,7 +401,7 @@ namespace { else break; - delta += delta / 2; + delta += 3 * delta / 8; assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE); } @@ -385,25 +409,14 @@ namespace { // Sort the PV lines searched so far and update the GUI std::stable_sort(RootMoves.begin(), RootMoves.begin() + PVIdx + 1); - if (PVIdx + 1 == MultiPV || Time::now() - SearchTime > 3000) + if (PVIdx + 1 == std::min(multiPV, RootMoves.size()) || Time::now() - SearchTime > 3000) sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl; } // If skill levels are enabled and time is up, pick a sub-optimal best move - if (skill.enabled() && skill.time_to_pick(depth)) + if (skill.candidates_size() && skill.time_to_pick(depth)) skill.pick_move(); - if (Options["Write Search Log"]) - { - RootMove& rm = RootMoves[0]; - if (skill.best != MOVE_NONE) - rm = *std::find(RootMoves.begin(), RootMoves.end(), skill.best); - - Log log(Options["Search Log Filename"]); - log << pretty_pv(pos, depth, rm.score, Time::now() - SearchTime, &rm.pv[0]) - << std::endl; - } - // Have we found a "mate in x"? if ( Limits.mate && bestValue >= VALUE_MATE_IN_MAX_PLY @@ -414,7 +427,7 @@ namespace { if (Limits.use_time_management() && !Signals.stop && !Signals.stopOnPonderhit) { // Take some extra time if the best move has changed - if (depth > 4 && depth < 50 && MultiPV == 1) + if (depth > 4 && multiPV == 1) TimeMgr.pv_instability(BestMoveChanges); // Stop the search if only one legal move is available or all @@ -540,6 +553,37 @@ namespace { return ttValue; } + // Step 4a. Tablebase probe + if ( !RootNode + && pos.total_piece_count() <= TBCardinality + && ( pos.total_piece_count() < TBCardinality || depth >= TBProbeDepth ) + && pos.rule50_count() == 0) + { + int found, v = Tablebases::probe_wdl(pos, &found); + + if (found) + { + TBHits++; + + if (TB50MoveRule) { + value = v < -1 ? -VALUE_MATE + MAX_PLY + ss->ply + : v > 1 ? VALUE_MATE - MAX_PLY - ss->ply + : VALUE_DRAW + 2 * v; + } + else + { + value = v < 0 ? -VALUE_MATE + MAX_PLY + ss->ply + : v > 0 ? VALUE_MATE - MAX_PLY - ss->ply + : VALUE_DRAW; + } + + TT.store(posKey, value_to_tt(value, ss->ply), BOUND_EXACT, + depth + 6 * ONE_PLY, MOVE_NONE, VALUE_NONE); + + return value; + } + } + // Step 5. Evaluate the position statically and update parent's gain statistics if (inCheck) { @@ -560,7 +604,9 @@ namespace { } else { - eval = ss->staticEval = evaluate(pos); + eval = ss->staticEval = + (ss-1)->currentMove != MOVE_NULL ? evaluate(pos) : -(ss-1)->staticEval + 2 * Eval::Tempo; + TT.store(posKey, VALUE_NONE, BOUND_NONE, DEPTH_NONE, MOVE_NONE, ss->staticEval); } @@ -568,6 +614,7 @@ namespace { && ss->staticEval != VALUE_NONE && (ss-1)->staticEval != VALUE_NONE && (move = (ss-1)->currentMove) != MOVE_NULL + && move != MOVE_NONE && type_of(move) == NORMAL) { Square to = to_sq(move); @@ -579,7 +626,6 @@ namespace { && depth < 4 * ONE_PLY && eval + razor_margin(depth) <= alpha && ttMove == MOVE_NONE - && abs(beta) < VALUE_MATE_IN_MAX_PLY && !pos.pawn_on_7th(pos.side_to_move())) { if ( depth <= ONE_PLY @@ -597,8 +643,7 @@ namespace { && !ss->skipNullMove && depth < 7 * ONE_PLY && eval - futility_margin(depth) >= beta - && abs(beta) < VALUE_MATE_IN_MAX_PLY - && abs(eval) < VALUE_KNOWN_WIN + && eval < VALUE_KNOWN_WIN // Do not return unproven wins && pos.non_pawn_material(pos.side_to_move())) return eval - futility_margin(depth); @@ -607,7 +652,6 @@ namespace { && !ss->skipNullMove && depth >= 2 * ONE_PLY && eval >= beta - && abs(beta) < VALUE_MATE_IN_MAX_PLY && pos.non_pawn_material(pos.side_to_move())) { ss->currentMove = MOVE_NULL; @@ -615,9 +659,7 @@ namespace { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and value - Depth R = 3 * ONE_PLY - + depth / 4 - + int(eval - beta) / PawnValueMg * ONE_PLY; + Depth R = (3 + depth / 4 + std::min(int(eval - beta) / PawnValueMg, 3)) * ONE_PLY; pos.do_null_move(st); (ss+1)->skipNullMove = true; @@ -632,7 +674,7 @@ namespace { if (nullValue >= VALUE_MATE_IN_MAX_PLY) nullValue = beta; - if (depth < 12 * ONE_PLY) + if (depth < 12 * ONE_PLY && abs(beta) < VALUE_KNOWN_WIN) return nullValue; // Do verification search at high depths @@ -682,10 +724,9 @@ namespace { && !ttMove && (PvNode || ss->staticEval + 256 >= beta)) { - Depth d = depth - 2 * ONE_PLY - (PvNode ? DEPTH_ZERO : depth / 4); - + Depth d = 2 * (depth - 2 * ONE_PLY) - (PvNode ? DEPTH_ZERO : depth / 2); ss->skipNullMove = true; - search(pos, ss, alpha, beta, d, true); + search(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(pos, ss, rBeta - 1, rBeta, depth / 2, cutNode); @@ -806,7 +846,7 @@ moves_loop: // When in check and at SpNode search starts from here { // Move count based pruning if ( depth < 16 * ONE_PLY - && moveCount >= FutilityMoveCounts[improving][depth] ) + && moveCount >= FutilityMoveCounts[improving][depth]) { if (SpNode) splitPoint->mutex.lock(); @@ -819,8 +859,8 @@ moves_loop: // When in check and at SpNode search starts from here // Futility pruning: parent node if (predictedDepth < 7 * ONE_PLY) { - futilityValue = ss->staticEval + futility_margin(predictedDepth) - + 128 + Gains[pos.moved_piece(move)][to_sq(move)]; + futilityValue = ss->staticEval + futility_margin(predictedDepth) + + 128 + Gains[pos.moved_piece(move)][to_sq(move)]; if (futilityValue <= alpha) { @@ -846,6 +886,9 @@ moves_loop: // When in check and at SpNode search starts from here } } + // Speculative prefetch as early as possible + prefetch((char*)TT.first_entry(pos.key_after(move))); + // Check for legality just before making the move if (!RootNode && !SpNode && !pos.legal(move, ci.pinned)) { @@ -872,15 +915,20 @@ moves_loop: // When in check and at SpNode search starts from here { ss->reduction = reduction(improving, depth, moveCount); - if (!PvNode && cutNode) + if ( (!PvNode && cutNode) + || History[pos.piece_on(to_sq(move))][to_sq(move)] < 0) ss->reduction += ONE_PLY; - else if (History[pos.piece_on(to_sq(move))][to_sq(move)] < 0) - ss->reduction += ONE_PLY / 2; - if (move == countermoves[0] || move == countermoves[1]) ss->reduction = std::max(DEPTH_ZERO, ss->reduction - ONE_PLY); + // Decrease reduction for moves that escape a capture + if ( ss->reduction + && type_of(move) == NORMAL + && type_of(pos.piece_on(to_sq(move))) != PAWN + && pos.see(make_move(to_sq(move), from_sq(move))) < 0) + ss->reduction = std::max(DEPTH_ZERO, ss->reduction - ONE_PLY); + Depth d = std::max(newDepth - ss->reduction, ONE_PLY); if (SpNode) alpha = splitPoint->alpha; @@ -906,20 +954,20 @@ moves_loop: // When in check and at SpNode search starts from here if (SpNode) alpha = splitPoint->alpha; - value = newDepth < ONE_PLY ? - givesCheck ? -qsearch(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO) - : -qsearch(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO) - : - search(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode); + value = newDepth < ONE_PLY ? + givesCheck ? -qsearch(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO) + : -qsearch(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO) + : - search(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode); } // For PV nodes only, do a full PV search on the first move or after a fail // high (in the latter case search only if value < beta), otherwise let the // parent node fail low with value <= alpha and to try another move. if (PvNode && (pvMove || (value > alpha && (RootNode || value < beta)))) - value = newDepth < ONE_PLY ? - givesCheck ? -qsearch(pos, ss+1, -beta, -alpha, DEPTH_ZERO) - : -qsearch(pos, ss+1, -beta, -alpha, DEPTH_ZERO) - : - search(pos, ss+1, -beta, -alpha, newDepth, false); + value = newDepth < ONE_PLY ? + givesCheck ? -qsearch(pos, ss+1, -beta, -alpha, DEPTH_ZERO) + : -qsearch(pos, ss+1, -beta, -alpha, DEPTH_ZERO) + : - search(pos, ss+1, -beta, -alpha, newDepth, false); // Step 17. Undo move pos.undo_move(move); @@ -994,8 +1042,8 @@ moves_loop: // When in check and at SpNode search starts from here { assert(bestValue > -VALUE_INFINITE && bestValue < beta); - thisThread->split(pos, ss, alpha, beta, &bestValue, &bestMove, - depth, moveCount, &mp, NT, cutNode); + thisThread->split(pos, ss, alpha, beta, &bestValue, &bestMove, + depth, moveCount, &mp, NT, cutNode); if (Signals.stop || thisThread->cutoff_occurred()) return VALUE_ZERO; @@ -1116,7 +1164,8 @@ moves_loop: // When in check and at SpNode search starts from here bestValue = ttValue; } else - ss->staticEval = bestValue = evaluate(pos); + ss->staticEval = bestValue = + (ss-1)->currentMove != MOVE_NULL ? evaluate(pos) : -(ss-1)->staticEval + 2 * Eval::Tempo; // Stand pat. Return immediately if static value is at least beta if (bestValue >= beta) @@ -1189,6 +1238,9 @@ moves_loop: // When in check and at SpNode search starts from here && pos.see_sign(move) < VALUE_ZERO) continue; + // Speculative prefetch as early as possible + prefetch((char*)TT.first_entry(pos.key_after(move))); + // Check for legality just before making the move if (!pos.legal(move, ci.pinned)) continue; @@ -1279,7 +1331,7 @@ moves_loop: // When in check and at SpNode search starts from here // Increase history value of the cut-off move and decrease all the other // played quiet moves. - Value bonus = Value(int(depth) * int(depth)); + Value bonus = Value(4 * int(depth) * int(depth)); History.update(pos.moved_piece(move), to_sq(move), bonus); for (int i = 0; i < quietsCnt; ++i) { @@ -1301,8 +1353,8 @@ moves_loop: // When in check and at SpNode search starts from here } - // When playing with a strength handicap, choose best move among the MultiPV - // set using a statistical rule dependent on 'level'. Idea by Heinz van Saanen. + // When playing with a strength handicap, choose best move among the first 'candidates' + // RootMoves using a statistical rule dependent on 'level'. Idea by Heinz van Saanen. Move Skill::pick_move() { @@ -1313,7 +1365,7 @@ moves_loop: // When in check and at SpNode search starts from here rk.rand(); // RootMoves are already sorted by score in descending order - int variance = std::min(RootMoves[0].score - RootMoves[MultiPV - 1].score, PawnValueMg); + int variance = std::min(RootMoves[0].score - RootMoves[candidates - 1].score, PawnValueMg); int weakness = 120 - 2 * level; int max_s = -VALUE_INFINITE; best = MOVE_NONE; @@ -1321,12 +1373,12 @@ moves_loop: // When in check and at SpNode search starts from here // Choose best move. For each move score we add two terms both dependent on // weakness. One deterministic and bigger for weaker moves, and one random, // then we choose the move with the resulting highest score. - for (size_t i = 0; i < MultiPV; ++i) + for (size_t i = 0; i < candidates; ++i) { int s = RootMoves[i].score; // Don't allow crazy blunders even at very low skills - if (i > 0 && RootMoves[i-1].score > s + 2 * PawnValueMg) + if (i > 0 && RootMoves[i - 1].score > s + 2 * PawnValueMg) break; // This is our magic formula @@ -1368,14 +1420,24 @@ moves_loop: // When in check and at SpNode search starts from here int d = updated ? depth : depth - 1; Value v = updated ? RootMoves[i].score : RootMoves[i].prevScore; + bool tb = RootInTB; + if (tb) + { + if (abs(v) >= VALUE_MATE - MAX_PLY) + tb = false; + else + v = TBScore; + } + if (ss.rdbuf()->in_avail()) // Not at first line ss << "\n"; ss << "info depth " << d << " seldepth " << selDepth - << " score " << (i == PVIdx ? score_to_uci(v, alpha, beta) : score_to_uci(v)) + << " score " << ((!tb && i == PVIdx) ? score_to_uci(v, alpha, beta) : score_to_uci(v)) << " nodes " << pos.nodes_searched() << " nps " << pos.nodes_searched() * 1000 / elapsed + << " tbhits " << TBHits << " time " << elapsed << " multipv " << i + 1 << " pv"; @@ -1463,46 +1525,13 @@ void Thread::idle_loop() { assert(!this_sp || (this_sp->masterThread == this && searching)); - while (true) + while (!exit) { - // If we are not searching, wait for a condition to be signaled instead of - // wasting CPU time polling for work. - while (!searching || exit) - { - if (exit) - { - assert(!this_sp); - return; - } - - // Grab the lock to avoid races with Thread::notify_one() - mutex.lock(); - - // If we are master and all slaves have finished then exit idle_loop - if (this_sp && this_sp->slavesMask.none()) - { - mutex.unlock(); - break; - } - - // Do sleep after retesting sleep conditions under lock protection. In - // particular we need to avoid a deadlock in case a master thread has, - // in the meanwhile, allocated us and sent the notify_one() call before - // we had the chance to grab the lock. - if (!searching && !exit) - sleepCondition.wait(mutex); - - mutex.unlock(); - } - // If this thread has been assigned work, launch a search - if (searching) + while (searching) { - assert(!exit); - Threads.mutex.lock(); - assert(searching); assert(activeSplitPoint); SplitPoint* sp = activeSplitPoint; @@ -1559,7 +1588,7 @@ void Thread::idle_loop() { if (Threads.size() > 2) for (size_t i = 0; i < Threads.size(); ++i) { - int size = Threads[i]->splitPointsSize; // Local copy + const int size = Threads[i]->splitPointsSize; // Local copy sp = size ? &Threads[i]->splitPoints[size - 1] : NULL; if ( sp @@ -1586,16 +1615,23 @@ void Thread::idle_loop() { } } - // If this thread is the master of a split point and all slaves have finished - // their work at this split point, return from the idle loop. + // Grab the lock to avoid races with Thread::notify_one() + mutex.lock(); + + // If we are master and all slaves have finished then exit idle_loop if (this_sp && this_sp->slavesMask.none()) { - this_sp->mutex.lock(); - bool finished = this_sp->slavesMask.none(); // Retest under lock protection - this_sp->mutex.unlock(); - if (finished) - return; + assert(!searching); + mutex.unlock(); + break; } + + // If we are not searching, wait for a condition to be signaled instead of + // wasting CPU time polling for work. + if (!searching && !exit) + sleepCondition.wait(mutex); + + mutex.unlock(); } } diff --git a/DroidFish/jni/stockfish/search.h b/DroidFish/jni/stockfish/search.h index a9f21fa..4fe5a5b 100644 --- a/DroidFish/jni/stockfish/search.h +++ b/DroidFish/jni/stockfish/search.h @@ -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 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 uint64_t perft(Position& pos, Depth depth); } // namespace Search diff --git a/DroidFish/jni/stockfish/tbcore.cpp b/DroidFish/jni/stockfish/tbcore.cpp new file mode 100644 index 0000000..6a2e814 --- /dev/null +++ b/DroidFish/jni/stockfish/tbcore.cpp @@ -0,0 +1,1620 @@ +/* + Copyright (c) 2011-2013 Ronald de Man + This file may be redistributed and/or modified without restrictions. + + tbcore.c contains engine-independent routines of the tablebase probing code. + This file should not need to much adaptation to add tablebase probing to + a particular engine, provided the engine is written in C or C++. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef __WIN32__ +#include +#endif +#include "tbcore.h" +#include + +#define TBMAX_PIECE 254 +#define TBMAX_PAWN 256 +#define HSHMAX 4 + +// for variants where kings can connect and/or captured +// #define CONNECTED_KINGS + +#define Swap(a,b) {int tmp=a;a=b;b=tmp;} + +#define TB_PAWN 1 +#define TB_KNIGHT 2 +#define TB_BISHOP 3 +#define TB_ROOK 4 +#define TB_QUEEN 5 +#define TB_KING 6 + +#define TB_WPAWN TB_PAWN +#define TB_BPAWN (TB_PAWN | 8) + +static std::mutex TB_mutex; + +static bool initialized = false; +static int num_paths = 0; +static char *path_string = NULL; +static char **paths = NULL; + +static int TBnum_piece, TBnum_pawn; +static struct TBEntry_piece TB_piece[TBMAX_PIECE]; +static struct TBEntry_pawn TB_pawn[TBMAX_PAWN]; + +static struct TBHashEntry WDL_hash[1 << TBHASHBITS][HSHMAX]; +static struct DTZTableEntry DTZ_hash[1 << TBHASHBITS][HSHMAX]; + +static void init_indices(void); +static uint64_t calc_key_from_pcs(const int *pcs, bool mirror); +static void free_wdl_entry(struct TBEntry *entry); +static void free_dtz_entry(struct TBEntry *entry); + +static FD open_tb(const char *str, const char *suffix) +{ + for (int i = 0; i < num_paths; i++) { + std::string file(paths[i]); + file += '/'; + file += str; + file += suffix; +#ifndef __WIN32__ + FD fd = open(file.c_str(), O_RDONLY); +#else + FD fd = CreateFile(file.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); +#endif + if (fd != FD_ERR) return fd; + } + return FD_ERR; +} + +static void close_tb(FD fd) +{ +#ifndef __WIN32__ + close(fd); +#else + CloseHandle(fd); +#endif +} + +static char *map_file(const char *name, const char *suffix, uint64_t *mapping) +{ + FD fd = open_tb(name, suffix); + if (fd == FD_ERR) + return NULL; +#ifndef __WIN32__ + struct stat statbuf; + fstat(fd, &statbuf); + *mapping = statbuf.st_size; + char *data = (char *)mmap(NULL, statbuf.st_size, PROT_READ, + MAP_SHARED, fd, 0); + if (data == (char *)(-1)) { + std::cout << "Could not mmap() " << name << std::endl; + close_tb(fd); + return NULL; + } +#else + DWORD size_low, size_high; + size_low = GetFileSize(fd, &size_high); + // *size = ((uint64_t)size_high) << 32 | ((uint64_t)size_low); + HANDLE map = CreateFileMapping(fd, NULL, PAGE_READONLY, size_high, size_low, + NULL); + if (map == NULL) { + std::cout << "CreateFileMapping() failed" << std::endl; + close_tb(fd); + return NULL; + } + *mapping = (uint64_t)map; + char *data = (char *)MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0); + if (data == NULL) { + std::cout << "MapViewOfFile() failed, name = " << name << suffix << ", error = " << GetLastError() << std::endl; + close_tb(fd); + return NULL; + } +#endif + close_tb(fd); + return data; +} + +#ifndef __WIN32__ +static void unmap_file(char *data, uint64_t size) +{ + if (!data) return; + munmap(data, size); +} +#else +static void unmap_file(char *data, uint64_t mapping) +{ + if (!data) return; + UnmapViewOfFile(data); + CloseHandle((HANDLE)mapping); +} +#endif + +static void add_to_hash(struct TBEntry *ptr, uint64_t key) +{ + int i, hshidx; + + hshidx = key >> (64 - TBHASHBITS); + i = 0; + while (i < HSHMAX && WDL_hash[hshidx][i].ptr) + i++; + assert(i < HSHMAX); + WDL_hash[hshidx][i].key = key; + WDL_hash[hshidx][i].ptr = ptr; +} + +static void add_to_dtz_hash(uint64_t key1, uint64_t key2) +{ + int i, hshidx; + + hshidx = key1 >> (64 - TBHASHBITS); + i = 0; + while (i < HSHMAX && DTZ_hash[hshidx][i].key1) + i++; + assert(i < HSHMAX); + DTZ_hash[hshidx][i].key1 = key1; + DTZ_hash[hshidx][i].key2 = key2; + DTZ_hash[hshidx][i].entry = NULL; +} + +static char pchr[] = {'K', 'Q', 'R', 'B', 'N', 'P'}; + +static void init_tb(char *str) +{ + FD fd; + struct TBEntry *entry; + int i, j, pcs[16]; + uint64_t key, key2; + int color; + char *s; + + fd = open_tb(str, WDLSUFFIX); + if (fd == FD_ERR) return; + close_tb(fd); + + for (i = 0; i < 16; i++) + pcs[i] = 0; + color = 0; + for (s = str; *s; s++) + switch (*s) { + case 'P': + pcs[TB_PAWN | color]++; + break; + case 'N': + pcs[TB_KNIGHT | color]++; + break; + case 'B': + pcs[TB_BISHOP | color]++; + break; + case 'R': + pcs[TB_ROOK | color]++; + break; + case 'Q': + pcs[TB_QUEEN | color]++; + break; + case 'K': + pcs[TB_KING | color]++; + break; + case 'v': + color = 0x08; + break; + } + for (i = 0; i < 8; i++) + if (pcs[i] != pcs[i+8]) + break; + key = calc_key_from_pcs(pcs, false); + key2 = calc_key_from_pcs(pcs, true); + if (pcs[TB_WPAWN] + pcs[TB_BPAWN] == 0) { + assert(TBnum_piece < TBMAX_PIECE); + entry = (struct TBEntry *)&TB_piece[TBnum_piece++]; + } else { + assert(TBnum_pawn < TBMAX_PAWN); + entry = (struct TBEntry *)&TB_pawn[TBnum_pawn++]; + } + entry->key = key; + entry->ready = 0; + entry->num = 0; + for (i = 0; i < 16; i++) + entry->num += pcs[i]; + entry->symmetric = (key == key2); + entry->has_pawns = (pcs[TB_WPAWN] + pcs[TB_BPAWN] > 0); + if (entry->num > Tablebases::TBLargest) + Tablebases::TBLargest = entry->num; + + if (entry->has_pawns) { + struct TBEntry_pawn *ptr = (struct TBEntry_pawn *)entry; + ptr->pawns[0] = pcs[TB_WPAWN]; + ptr->pawns[1] = pcs[TB_BPAWN]; + if (pcs[TB_BPAWN] > 0 + && (pcs[TB_WPAWN] == 0 || pcs[TB_BPAWN] < pcs[TB_WPAWN])) { + ptr->pawns[0] = pcs[TB_BPAWN]; + ptr->pawns[1] = pcs[TB_WPAWN]; + } + } else { + struct TBEntry_piece *ptr = (struct TBEntry_piece *)entry; + for (i = 0, j = 0; i < 16; i++) + if (pcs[i] == 1) j++; + if (j >= 3) ptr->enc_type = 0; + else if (j == 2) ptr->enc_type = 2; + else { /* only for suicide */ + j = 16; + for (i = 0; i < 16; i++) { + if (pcs[i] < j && pcs[i] > 1) j = pcs[i]; + ptr->enc_type = 1 + j; + } + } + } + add_to_hash(entry, key); + if (key2 != key) add_to_hash(entry, key2); + add_to_dtz_hash(key, key2); +} + +void Tablebases::init(const std::string& path) +{ + char str[16]; + int i, j, k, l; + + { // The probing code currently expects a little-endian architecture + static_assert(sizeof(uint32_t) == 4, "Unsupported architecture"); + uint32_t test = 0x01020304; + unsigned char* p = (unsigned char*)&test; + if (p[0] != 4 || p[1] != 3 || p[2] != 2 || p[3] != 1) + return; + } + + if (initialized) { + free(path_string); path_string = NULL; + free(paths); paths = NULL; + struct TBEntry *entry; + for (i = 0; i < TBnum_piece; i++) { + entry = (struct TBEntry *)&TB_piece[i]; + free_wdl_entry(entry); + } + for (i = 0; i < TBnum_pawn; i++) { + entry = (struct TBEntry *)&TB_pawn[i]; + free_wdl_entry(entry); + } + for (i = 0; i < (1 << TBHASHBITS); i++) + for (j = 0; j < HSHMAX; j++) { + if (DTZ_hash[i][j].entry) { + free_dtz_entry(DTZ_hash[i][j].entry); + DTZ_hash[i][j].entry = NULL; + } + } + TBnum_piece = TBnum_pawn = 0; + TBLargest = 0; + } else { + init_indices(); + initialized = true; + } + + const char *p = path.c_str(); + if (strlen(p) == 0) + return; + path_string = (char *)malloc(strlen(p) + 1); + strcpy(path_string, p); + num_paths = 0; + for (i = 0;; i++) { + if (path_string[i] && path_string[i] != SEP_CHAR) + num_paths++; + while (path_string[i] && path_string[i] != SEP_CHAR) + i++; + if (!path_string[i]) break; + path_string[i] = 0; + } + paths = (char **)malloc(num_paths * sizeof(char *)); + for (i = j = 0; i < num_paths; i++) { + while (!path_string[j]) j++; + paths[i] = &path_string[j]; + while (path_string[j]) j++; + } + + for (i = 0; i < (1 << TBHASHBITS); i++) + for (j = 0; j < HSHMAX; j++) { + WDL_hash[i][j].key = 0ULL; + WDL_hash[i][j].ptr = NULL; + } + + for (i = 0; i < (1 << TBHASHBITS); i++) + for (j = 0; j < HSHMAX; j++) { + DTZ_hash[i][j].key1 = 0ULL; + DTZ_hash[i][j].key2 = 0ULL; + DTZ_hash[i][j].entry = NULL; + } + + for (i = 1; i < 6; i++) { + sprintf(str, "K%cvK", pchr[i]); + init_tb(str); + } + + for (i = 1; i < 6; i++) + for (j = i; j < 6; j++) { + sprintf(str, "K%cvK%c", pchr[i], pchr[j]); + init_tb(str); + } + + for (i = 1; i < 6; i++) + for (j = i; j < 6; j++) { + sprintf(str, "K%c%cvK", pchr[i], pchr[j]); + init_tb(str); + } + + for (i = 1; i < 6; i++) + for (j = i; j < 6; j++) + for (k = 1; k < 6; k++) { + sprintf(str, "K%c%cvK%c", pchr[i], pchr[j], pchr[k]); + init_tb(str); + } + + for (i = 1; i < 6; i++) + for (j = i; j < 6; j++) + for (k = j; k < 6; k++) { + sprintf(str, "K%c%c%cvK", pchr[i], pchr[j], pchr[k]); + init_tb(str); + } + + // 6-piece tables are only supported for 64-bit, because tables are mmap()ed into memory + if (sizeof(char*) >= 8) { + for (i = 1; i < 6; i++) + for (j = i; j < 6; j++) + for (k = i; k < 6; k++) + for (l = (i == k) ? j : k; l < 6; l++) { + sprintf(str, "K%c%cvK%c%c", pchr[i], pchr[j], pchr[k], pchr[l]); + init_tb(str); + } + + for (i = 1; i < 6; i++) + for (j = i; j < 6; j++) + for (k = j; k < 6; k++) + for (l = 1; l < 6; l++) { + sprintf(str, "K%c%c%cvK%c", pchr[i], pchr[j], pchr[k], pchr[l]); + init_tb(str); + } + + for (i = 1; i < 6; i++) + for (j = i; j < 6; j++) + for (k = j; k < 6; k++) + for (l = k; l < 6; l++) { + sprintf(str, "K%c%c%c%cvK", pchr[i], pchr[j], pchr[k], pchr[l]); + init_tb(str); + } + } + + std::cout << "info string Found " << (TBnum_piece + TBnum_pawn) << " syzygy tablebases" << std::endl; +} + +static const signed char offdiag[] = { + 0,-1,-1,-1,-1,-1,-1,-1, + 1, 0,-1,-1,-1,-1,-1,-1, + 1, 1, 0,-1,-1,-1,-1,-1, + 1, 1, 1, 0,-1,-1,-1,-1, + 1, 1, 1, 1, 0,-1,-1,-1, + 1, 1, 1, 1, 1, 0,-1,-1, + 1, 1, 1, 1, 1, 1, 0,-1, + 1, 1, 1, 1, 1, 1, 1, 0 +}; + +static const ubyte triangle[] = { + 6, 0, 1, 2, 2, 1, 0, 6, + 0, 7, 3, 4, 4, 3, 7, 0, + 1, 3, 8, 5, 5, 8, 3, 1, + 2, 4, 5, 9, 9, 5, 4, 2, + 2, 4, 5, 9, 9, 5, 4, 2, + 1, 3, 8, 5, 5, 8, 3, 1, + 0, 7, 3, 4, 4, 3, 7, 0, + 6, 0, 1, 2, 2, 1, 0, 6 +}; + +static const ubyte invtriangle[] = { + 1, 2, 3, 10, 11, 19, 0, 9, 18, 27 +}; + +static const ubyte invdiag[] = { + 0, 9, 18, 27, 36, 45, 54, 63, + 7, 14, 21, 28, 35, 42, 49, 56 +}; + +static const ubyte flipdiag[] = { + 0, 8, 16, 24, 32, 40, 48, 56, + 1, 9, 17, 25, 33, 41, 49, 57, + 2, 10, 18, 26, 34, 42, 50, 58, + 3, 11, 19, 27, 35, 43, 51, 59, + 4, 12, 20, 28, 36, 44, 52, 60, + 5, 13, 21, 29, 37, 45, 53, 61, + 6, 14, 22, 30, 38, 46, 54, 62, + 7, 15, 23, 31, 39, 47, 55, 63 +}; + +static const ubyte lower[] = { + 28, 0, 1, 2, 3, 4, 5, 6, + 0, 29, 7, 8, 9, 10, 11, 12, + 1, 7, 30, 13, 14, 15, 16, 17, + 2, 8, 13, 31, 18, 19, 20, 21, + 3, 9, 14, 18, 32, 22, 23, 24, + 4, 10, 15, 19, 22, 33, 25, 26, + 5, 11, 16, 20, 23, 25, 34, 27, + 6, 12, 17, 21, 24, 26, 27, 35 +}; + +static const ubyte diag[] = { + 0, 0, 0, 0, 0, 0, 0, 8, + 0, 1, 0, 0, 0, 0, 9, 0, + 0, 0, 2, 0, 0, 10, 0, 0, + 0, 0, 0, 3, 11, 0, 0, 0, + 0, 0, 0, 12, 4, 0, 0, 0, + 0, 0, 13, 0, 0, 5, 0, 0, + 0, 14, 0, 0, 0, 0, 6, 0, + 15, 0, 0, 0, 0, 0, 0, 7 +}; + +static const ubyte flap[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 6, 12, 18, 18, 12, 6, 0, + 1, 7, 13, 19, 19, 13, 7, 1, + 2, 8, 14, 20, 20, 14, 8, 2, + 3, 9, 15, 21, 21, 15, 9, 3, + 4, 10, 16, 22, 22, 16, 10, 4, + 5, 11, 17, 23, 23, 17, 11, 5, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const ubyte ptwist[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 47, 35, 23, 11, 10, 22, 34, 46, + 45, 33, 21, 9, 8, 20, 32, 44, + 43, 31, 19, 7, 6, 18, 30, 42, + 41, 29, 17, 5, 4, 16, 28, 40, + 39, 27, 15, 3, 2, 14, 26, 38, + 37, 25, 13, 1, 0, 12, 24, 36, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const ubyte invflap[] = { + 8, 16, 24, 32, 40, 48, + 9, 17, 25, 33, 41, 49, + 10, 18, 26, 34, 42, 50, + 11, 19, 27, 35, 43, 51 +}; + +static const ubyte invptwist[] = { + 52, 51, 44, 43, 36, 35, 28, 27, 20, 19, 12, 11, + 53, 50, 45, 42, 37, 34, 29, 26, 21, 18, 13, 10, + 54, 49, 46, 41, 38, 33, 30, 25, 22, 17, 14, 9, + 55, 48, 47, 40, 39, 32, 31, 24, 23, 16, 15, 8 +}; + +static const ubyte file_to_file[] = { + 0, 1, 2, 3, 3, 2, 1, 0 +}; + +#ifndef CONNECTED_KINGS +static const short KK_idx[10][64] = { + { -1, -1, -1, 0, 1, 2, 3, 4, + -1, -1, -1, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57 }, + { 58, -1, -1, -1, 59, 60, 61, 62, + 63, -1, -1, -1, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, + 100,101,102,103,104,105,106,107, + 108,109,110,111,112,113,114,115}, + {116,117, -1, -1, -1,118,119,120, + 121,122, -1, -1, -1,123,124,125, + 126,127,128,129,130,131,132,133, + 134,135,136,137,138,139,140,141, + 142,143,144,145,146,147,148,149, + 150,151,152,153,154,155,156,157, + 158,159,160,161,162,163,164,165, + 166,167,168,169,170,171,172,173 }, + {174, -1, -1, -1,175,176,177,178, + 179, -1, -1, -1,180,181,182,183, + 184, -1, -1, -1,185,186,187,188, + 189,190,191,192,193,194,195,196, + 197,198,199,200,201,202,203,204, + 205,206,207,208,209,210,211,212, + 213,214,215,216,217,218,219,220, + 221,222,223,224,225,226,227,228 }, + {229,230, -1, -1, -1,231,232,233, + 234,235, -1, -1, -1,236,237,238, + 239,240, -1, -1, -1,241,242,243, + 244,245,246,247,248,249,250,251, + 252,253,254,255,256,257,258,259, + 260,261,262,263,264,265,266,267, + 268,269,270,271,272,273,274,275, + 276,277,278,279,280,281,282,283 }, + {284,285,286,287,288,289,290,291, + 292,293, -1, -1, -1,294,295,296, + 297,298, -1, -1, -1,299,300,301, + 302,303, -1, -1, -1,304,305,306, + 307,308,309,310,311,312,313,314, + 315,316,317,318,319,320,321,322, + 323,324,325,326,327,328,329,330, + 331,332,333,334,335,336,337,338 }, + { -1, -1,339,340,341,342,343,344, + -1, -1,345,346,347,348,349,350, + -1, -1,441,351,352,353,354,355, + -1, -1, -1,442,356,357,358,359, + -1, -1, -1, -1,443,360,361,362, + -1, -1, -1, -1, -1,444,363,364, + -1, -1, -1, -1, -1, -1,445,365, + -1, -1, -1, -1, -1, -1, -1,446 }, + { -1, -1, -1,366,367,368,369,370, + -1, -1, -1,371,372,373,374,375, + -1, -1, -1,376,377,378,379,380, + -1, -1, -1,447,381,382,383,384, + -1, -1, -1, -1,448,385,386,387, + -1, -1, -1, -1, -1,449,388,389, + -1, -1, -1, -1, -1, -1,450,390, + -1, -1, -1, -1, -1, -1, -1,451 }, + {452,391,392,393,394,395,396,397, + -1, -1, -1, -1,398,399,400,401, + -1, -1, -1, -1,402,403,404,405, + -1, -1, -1, -1,406,407,408,409, + -1, -1, -1, -1,453,410,411,412, + -1, -1, -1, -1, -1,454,413,414, + -1, -1, -1, -1, -1, -1,455,415, + -1, -1, -1, -1, -1, -1, -1,456 }, + {457,416,417,418,419,420,421,422, + -1,458,423,424,425,426,427,428, + -1, -1, -1, -1, -1,429,430,431, + -1, -1, -1, -1, -1,432,433,434, + -1, -1, -1, -1, -1,435,436,437, + -1, -1, -1, -1, -1,459,438,439, + -1, -1, -1, -1, -1, -1,460,440, + -1, -1, -1, -1, -1, -1, -1,461 } +}; +#else +static const short PP_idx[10][64] = { + { 0, -1, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, + -1, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61 }, + { 62, -1, -1, 63, 64, 65, -1, 66, + -1, 67, 68, 69, 70, 71, 72, -1, + 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 95, 96, + -1, 97, 98, 99,100,101,102,103, + -1,104,105,106,107,108,109, -1, + 110, -1,111,112,113,114, -1,115 }, + {116, -1, -1, -1,117, -1, -1,118, + -1,119,120,121,122,123,124, -1, + -1,125,126,127,128,129,130, -1, + 131,132,133,134,135,136,137,138, + -1,139,140,141,142,143,144,145, + -1,146,147,148,149,150,151, -1, + -1,152,153,154,155,156,157, -1, + 158, -1, -1,159,160, -1, -1,161 }, + {162, -1, -1, -1, -1, -1, -1,163, + -1,164, -1,165,166,167,168, -1, + -1,169,170,171,172,173,174, -1, + -1,175,176,177,178,179,180, -1, + -1,181,182,183,184,185,186, -1, + -1, -1,187,188,189,190,191, -1, + -1,192,193,194,195,196,197, -1, + 198, -1, -1, -1, -1, -1, -1,199 }, + {200, -1, -1, -1, -1, -1, -1,201, + -1,202, -1, -1,203, -1,204, -1, + -1, -1,205,206,207,208, -1, -1, + -1,209,210,211,212,213,214, -1, + -1, -1,215,216,217,218,219, -1, + -1, -1,220,221,222,223, -1, -1, + -1,224, -1,225,226, -1,227, -1, + 228, -1, -1, -1, -1, -1, -1,229 }, + {230, -1, -1, -1, -1, -1, -1,231, + -1,232, -1, -1, -1, -1,233, -1, + -1, -1,234, -1,235,236, -1, -1, + -1, -1,237,238,239,240, -1, -1, + -1, -1, -1,241,242,243, -1, -1, + -1, -1,244,245,246,247, -1, -1, + -1,248, -1, -1, -1, -1,249, -1, + 250, -1, -1, -1, -1, -1, -1,251 }, + { -1, -1, -1, -1, -1, -1, -1,259, + -1,252, -1, -1, -1, -1,260, -1, + -1, -1,253, -1, -1,261, -1, -1, + -1, -1, -1,254,262, -1, -1, -1, + -1, -1, -1, -1,255, -1, -1, -1, + -1, -1, -1, -1, -1,256, -1, -1, + -1, -1, -1, -1, -1, -1,257, -1, + -1, -1, -1, -1, -1, -1, -1,258 }, + { -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1,268, -1, + -1, -1,263, -1, -1,269, -1, -1, + -1, -1, -1,264,270, -1, -1, -1, + -1, -1, -1, -1,265, -1, -1, -1, + -1, -1, -1, -1, -1,266, -1, -1, + -1, -1, -1, -1, -1, -1,267, -1, + -1, -1, -1, -1, -1, -1, -1, -1 }, + { -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1,274, -1, -1, + -1, -1, -1,271,275, -1, -1, -1, + -1, -1, -1, -1,272, -1, -1, -1, + -1, -1, -1, -1, -1,273, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 }, + { -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1,277, -1, -1, -1, + -1, -1, -1, -1,276, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 } +}; + +static const ubyte test45[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 0, 0, 0, 0, 0, + 1, 1, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const ubyte mtwist[] = { + 15, 63, 55, 47, 40, 48, 56, 12, + 62, 11, 39, 31, 24, 32, 8, 57, + 54, 38, 7, 23, 16, 4, 33, 49, + 46, 30, 22, 3, 0, 17, 25, 41, + 45, 29, 21, 2, 1, 18, 26, 42, + 53, 37, 6, 20, 19, 5, 34, 50, + 61, 10, 36, 28, 27, 35, 9, 58, + 14, 60, 52, 44, 43, 51, 59, 13 +}; +#endif + +static int binomial[5][64]; +static int pawnidx[5][24]; +static int pfactor[5][4]; +#ifdef CONNECTED_KINGS +static int multidx[5][10]; +static int mfactor[5]; +#endif + +static void init_indices(void) +{ + int i, j, k; + + // binomial[k-1][n] = Bin(n, k) + for (i = 0; i < 5; i++) + for (j = 0; j < 64; j++) { + int f = j; + int l = 1; + for (k = 1; k <= i; k++) { + f *= (j - k); + l *= (k + 1); + } + binomial[i][j] = f / l; + } + + for (i = 0; i < 5; i++) { + int s = 0; + for (j = 0; j < 6; j++) { + pawnidx[i][j] = s; + s += (i == 0) ? 1 : binomial[i - 1][ptwist[invflap[j]]]; + } + pfactor[i][0] = s; + s = 0; + for (; j < 12; j++) { + pawnidx[i][j] = s; + s += (i == 0) ? 1 : binomial[i - 1][ptwist[invflap[j]]]; + } + pfactor[i][1] = s; + s = 0; + for (; j < 18; j++) { + pawnidx[i][j] = s; + s += (i == 0) ? 1 : binomial[i - 1][ptwist[invflap[j]]]; + } + pfactor[i][2] = s; + s = 0; + for (; j < 24; j++) { + pawnidx[i][j] = s; + s += (i == 0) ? 1 : binomial[i - 1][ptwist[invflap[j]]]; + } + pfactor[i][3] = s; + } + +#ifdef CONNECTED_KINGS + for (i = 0; i < 5; i++) { + int s = 0; + for (j = 0; j < 10; j++) { + multidx[i][j] = s; + s += (i == 0) ? 1 : binomial[i - 1][mtwist[invtriangle[j]]]; + } + mfactor[i] = s; + } +#endif +} + +#ifndef CONNECTED_KINGS +static uint64_t encode_piece(struct TBEntry_piece *ptr, ubyte *norm, int *pos, int *factor) +{ + uint64_t idx = 0; + int i, j, k, m, l, p; + int n = ptr->num; + + if (pos[0] & 0x04) { + for (i = 0; i < n; i++) + pos[i] ^= 0x07; + } + if (pos[0] & 0x20) { + for (i = 0; i < n; i++) + pos[i] ^= 0x38; + } + + for (i = 0; i < n; i++) + if (offdiag[pos[i]]) break; + if (i < (ptr->enc_type == 0 ? 3 : 2) && offdiag[pos[i]] > 0) + for (i = 0; i < n; i++) + pos[i] = flipdiag[pos[i]]; + + switch (ptr->enc_type) { + + case 0: /* 111 */ + i = (pos[1] > pos[0]); + j = (pos[2] > pos[0]) + (pos[2] > pos[1]); + + if (offdiag[pos[0]]) + idx = triangle[pos[0]] * 63*62 + (pos[1] - i) * 62 + (pos[2] - j); + else if (offdiag[pos[1]]) + idx = 6*63*62 + diag[pos[0]] * 28*62 + lower[pos[1]] * 62 + pos[2] - j; + else if (offdiag[pos[2]]) + idx = 6*63*62 + 4*28*62 + (diag[pos[0]]) * 7*28 + (diag[pos[1]] - i) * 28 + lower[pos[2]]; + else + idx = 6*63*62 + 4*28*62 + 4*7*28 + (diag[pos[0]] * 7*6) + (diag[pos[1]] - i) * 6 + (diag[pos[2]] - j); + i = 3; + break; + + case 1: /* K3 */ + j = (pos[2] > pos[0]) + (pos[2] > pos[1]); + + idx = KK_idx[triangle[pos[0]]][pos[1]]; + if (idx < 441) + idx = idx + 441 * (pos[2] - j); + else { + idx = 441*62 + (idx - 441) + 21 * lower[pos[2]]; + if (!offdiag[pos[2]]) + idx -= j * 21; + } + i = 3; + break; + + default: /* K2 */ + idx = KK_idx[triangle[pos[0]]][pos[1]]; + i = 2; + break; + } + idx *= factor[0]; + + for (; i < n;) { + int t = norm[i]; + for (j = i; j < i + t; j++) + for (k = j + 1; k < i + t; k++) + if (pos[j] > pos[k]) Swap(pos[j], pos[k]); + int s = 0; + for (m = i; m < i + t; m++) { + p = pos[m]; + for (l = 0, j = 0; l < i; l++) + j += (p > pos[l]); + s += binomial[m - i][p - j]; + } + idx += ((uint64_t)s) * ((uint64_t)factor[i]); + i += t; + } + + return idx; +} +#else +static uint64_t encode_piece(struct TBEntry_piece *ptr, ubyte *norm, int *pos, int *factor) +{ + uint64_t idx; + int i, j, k, m, l, p; + int n = ptr->num; + + if (ptr->enc_type < 3) { + if (pos[0] & 0x04) { + for (i = 0; i < n; i++) + pos[i] ^= 0x07; + } + if (pos[0] & 0x20) { + for (i = 0; i < n; i++) + pos[i] ^= 0x38; + } + + for (i = 0; i < n; i++) + if (offdiag[pos[i]]) break; + if (i < (ptr->enc_type == 0 ? 3 : 2) && offdiag[pos[i]] > 0) + for (i = 0; i < n; i++) + pos[i] = flipdiag[pos[i]]; + + switch (ptr->enc_type) { + + case 0: /* 111 */ + i = (pos[1] > pos[0]); + j = (pos[2] > pos[0]) + (pos[2] > pos[1]); + + if (offdiag[pos[0]]) + idx = triangle[pos[0]] * 63*62 + (pos[1] - i) * 62 + (pos[2] - j); + else if (offdiag[pos[1]]) + idx = 6*63*62 + diag[pos[0]] * 28*62 + lower[pos[1]] * 62 + pos[2] - j; + else if (offdiag[pos[2]]) + idx = 6*63*62 + 4*28*62 + (diag[pos[0]]) * 7*28 + (diag[pos[1]] - i) * 28 + lower[pos[2]]; + else + idx = 6*63*62 + 4*28*62 + 4*7*28 + (diag[pos[0]] * 7*6) + (diag[pos[1]] - i) * 6 + (diag[pos[2]] - j); + i = 3; + break; + + case 2: /* 11 */ + i = (pos[1] > pos[0]); + + if (offdiag[pos[0]]) + idx = triangle[pos[0]] * 63 + (pos[1] - i); + else if (offdiag[pos[1]]) + idx = 6*63 + diag[pos[0]] * 28 + lower[pos[1]]; + else + idx = 6*63 + 4*28 + (diag[pos[0]]) * 7 + (diag[pos[1]] - i); + i = 2; + break; + + } + } else if (ptr->enc_type == 3) { /* 2, e.g. KKvK */ + if (triangle[pos[0]] > triangle[pos[1]]) + Swap(pos[0], pos[1]); + if (pos[0] & 0x04) + for (i = 0; i < n; i++) + pos[i] ^= 0x07; + if (pos[0] & 0x20) + for (i = 0; i < n; i++) + pos[i] ^= 0x38; + if (offdiag[pos[0]] > 0 || (offdiag[pos[0]] == 0 && offdiag[pos[1]] > 0)) + for (i = 0; i < n; i++) + pos[i] = flipdiag[pos[i]]; + if (test45[pos[1]] && triangle[pos[0]] == triangle[pos[1]]) { + Swap(pos[0], pos[1]); + for (i = 0; i < n; i++) + pos[i] = flipdiag[pos[i] ^ 0x38]; + } + idx = PP_idx[triangle[pos[0]]][pos[1]]; + i = 2; + } else { /* 3 and higher, e.g. KKKvK and KKKKvK */ + for (i = 1; i < norm[0]; i++) + if (triangle[pos[0]] > triangle[pos[i]]) + Swap(pos[0], pos[i]); + if (pos[0] & 0x04) + for (i = 0; i < n; i++) + pos[i] ^= 0x07; + if (pos[0] & 0x20) + for (i = 0; i < n; i++) + pos[i] ^= 0x38; + if (offdiag[pos[0]] > 0) + for (i = 0; i < n; i++) + pos[i] = flipdiag[pos[i]]; + for (i = 1; i < norm[0]; i++) + for (j = i + 1; j < norm[0]; j++) + if (mtwist[pos[i]] > mtwist[pos[j]]) + Swap(pos[i], pos[j]); + + idx = multidx[norm[0] - 1][triangle[pos[0]]]; + for (i = 1; i < norm[0]; i++) + idx += binomial[i - 1][mtwist[pos[i]]]; + } + idx *= factor[0]; + + for (; i < n;) { + int t = norm[i]; + for (j = i; j < i + t; j++) + for (k = j + 1; k < i + t; k++) + if (pos[j] > pos[k]) Swap(pos[j], pos[k]); + int s = 0; + for (m = i; m < i + t; m++) { + p = pos[m]; + for (l = 0, j = 0; l < i; l++) + j += (p > pos[l]); + s += binomial[m - i][p - j]; + } + idx += ((uint64_t)s) * ((uint64_t)factor[i]); + i += t; + } + + return idx; +} +#endif + +// determine file of leftmost pawn and sort pawns +static int pawn_file(struct TBEntry_pawn *ptr, int *pos) +{ + int i; + + for (i = 1; i < ptr->pawns[0]; i++) + if (flap[pos[0]] > flap[pos[i]]) + Swap(pos[0], pos[i]); + + return file_to_file[pos[0] & 0x07]; +} + +static uint64_t encode_pawn(struct TBEntry_pawn *ptr, ubyte *norm, int *pos, int *factor) +{ + uint64_t idx; + int i, j, k, m, s, t; + int n = ptr->num; + + if (pos[0] & 0x04) + for (i = 0; i < n; i++) + pos[i] ^= 0x07; + + for (i = 1; i < ptr->pawns[0]; i++) + for (j = i + 1; j < ptr->pawns[0]; j++) + if (ptwist[pos[i]] < ptwist[pos[j]]) + Swap(pos[i], pos[j]); + + t = ptr->pawns[0] - 1; + idx = pawnidx[t][flap[pos[0]]]; + for (i = t; i > 0; i--) + idx += binomial[t - i][ptwist[pos[i]]]; + idx *= factor[0]; + + // remaining pawns + i = ptr->pawns[0]; + t = i + ptr->pawns[1]; + if (t > i) { + for (j = i; j < t; j++) + for (k = j + 1; k < t; k++) + if (pos[j] > pos[k]) Swap(pos[j], pos[k]); + s = 0; + for (m = i; m < t; m++) { + int p = pos[m]; + for (k = 0, j = 0; k < i; k++) + j += (p > pos[k]); + s += binomial[m - i][p - j - 8]; + } + idx += ((uint64_t)s) * ((uint64_t)factor[i]); + i = t; + } + + for (; i < n;) { + t = norm[i]; + for (j = i; j < i + t; j++) + for (k = j + 1; k < i + t; k++) + if (pos[j] > pos[k]) Swap(pos[j], pos[k]); + s = 0; + for (m = i; m < i + t; m++) { + int p = pos[m]; + for (k = 0, j = 0; k < i; k++) + j += (p > pos[k]); + s += binomial[m - i][p - j]; + } + idx += ((uint64_t)s) * ((uint64_t)factor[i]); + i += t; + } + + return idx; +} + +static ubyte decompress_pairs(struct PairsData *d, uint64_t index); + +// place k like pieces on n squares +static int subfactor(int k, int n) +{ + int i, f, l; + + f = n; + l = 1; + for (i = 1; i < k; i++) { + f *= n - i; + l *= i + 1; + } + + return f / l; +} + +static uint64_t calc_factors_piece(int *factor, int num, int order, ubyte *norm, ubyte enc_type) +{ + int i, k, n; + uint64_t f; +#ifndef CONNECTED_KINGS + static int pivfac[] = { 31332, 28056, 462 }; +#else + static int pivfac[] = { 31332, 0, 518, 278 }; +#endif + + n = 64 - norm[0]; + + f = 1; + for (i = norm[0], k = 0; i < num || k == order; k++) { + if (k == order) { + factor[0] = f; +#ifndef CONNECTED_KINGS + f *= pivfac[enc_type]; +#else + if (enc_type < 4) + f *= pivfac[enc_type]; + else + f *= mfactor[enc_type - 2]; +#endif + } else { + factor[i] = f; + f *= subfactor(norm[i], n); + n -= norm[i]; + i += norm[i]; + } + } + + return f; +} + +static uint64_t calc_factors_pawn(int *factor, int num, int order, int order2, ubyte *norm, int file) +{ + int i, k, n; + uint64_t f; + + i = norm[0]; + if (order2 < 0x0f) i += norm[i]; + n = 64 - i; + + f = 1; + for (k = 0; i < num || k == order || k == order2; k++) { + if (k == order) { + factor[0] = f; + f *= pfactor[norm[0] - 1][file]; + } else if (k == order2) { + factor[norm[0]] = f; + f *= subfactor(norm[norm[0]], 48 - norm[0]); + } else { + factor[i] = f; + f *= subfactor(norm[i], n); + n -= norm[i]; + i += norm[i]; + } + } + + return f; +} + +static void set_norm_piece(struct TBEntry_piece *ptr, ubyte *norm, ubyte *pieces) +{ + int i, j; + + for (i = 0; i < ptr->num; i++) + norm[i] = 0; + + switch (ptr->enc_type) { + case 0: + norm[0] = 3; + break; + case 2: + norm[0] = 2; + break; + default: + norm[0] = ptr->enc_type - 1; + break; + } + + for (i = norm[0]; i < ptr->num; i += norm[i]) + for (j = i; j < ptr->num && pieces[j] == pieces[i]; j++) + norm[i]++; +} + +static void set_norm_pawn(struct TBEntry_pawn *ptr, ubyte *norm, ubyte *pieces) +{ + int i, j; + + for (i = 0; i < ptr->num; i++) + norm[i] = 0; + + norm[0] = ptr->pawns[0]; + if (ptr->pawns[1]) norm[ptr->pawns[0]] = ptr->pawns[1]; + + for (i = ptr->pawns[0] + ptr->pawns[1]; i < ptr->num; i += norm[i]) + for (j = i; j < ptr->num && pieces[j] == pieces[i]; j++) + norm[i]++; +} + +static void setup_pieces_piece(struct TBEntry_piece *ptr, unsigned char *data, uint64_t *tb_size) +{ + int i; + int order; + + for (i = 0; i < ptr->num; i++) + ptr->pieces[0][i] = data[i + 1] & 0x0f; + order = data[0] & 0x0f; + set_norm_piece(ptr, ptr->norm[0], ptr->pieces[0]); + tb_size[0] = calc_factors_piece(ptr->factor[0], ptr->num, order, ptr->norm[0], ptr->enc_type); + + for (i = 0; i < ptr->num; i++) + ptr->pieces[1][i] = data[i + 1] >> 4; + order = data[0] >> 4; + set_norm_piece(ptr, ptr->norm[1], ptr->pieces[1]); + tb_size[1] = calc_factors_piece(ptr->factor[1], ptr->num, order, ptr->norm[1], ptr->enc_type); +} + +static void setup_pieces_piece_dtz(struct DTZEntry_piece *ptr, unsigned char *data, uint64_t *tb_size) +{ + int i; + int order; + + for (i = 0; i < ptr->num; i++) + ptr->pieces[i] = data[i + 1] & 0x0f; + order = data[0] & 0x0f; + set_norm_piece((struct TBEntry_piece *)ptr, ptr->norm, ptr->pieces); + tb_size[0] = calc_factors_piece(ptr->factor, ptr->num, order, ptr->norm, ptr->enc_type); +} + +static void setup_pieces_pawn(struct TBEntry_pawn *ptr, unsigned char *data, uint64_t *tb_size, int f) +{ + int i, j; + int order, order2; + + j = 1 + (ptr->pawns[1] > 0); + order = data[0] & 0x0f; + order2 = ptr->pawns[1] ? (data[1] & 0x0f) : 0x0f; + for (i = 0; i < ptr->num; i++) + ptr->file[f].pieces[0][i] = data[i + j] & 0x0f; + set_norm_pawn(ptr, ptr->file[f].norm[0], ptr->file[f].pieces[0]); + tb_size[0] = calc_factors_pawn(ptr->file[f].factor[0], ptr->num, order, order2, ptr->file[f].norm[0], f); + + order = data[0] >> 4; + order2 = ptr->pawns[1] ? (data[1] >> 4) : 0x0f; + for (i = 0; i < ptr->num; i++) + ptr->file[f].pieces[1][i] = data[i + j] >> 4; + set_norm_pawn(ptr, ptr->file[f].norm[1], ptr->file[f].pieces[1]); + tb_size[1] = calc_factors_pawn(ptr->file[f].factor[1], ptr->num, order, order2, ptr->file[f].norm[1], f); +} + +static void setup_pieces_pawn_dtz(struct DTZEntry_pawn *ptr, unsigned char *data, uint64_t *tb_size, int f) +{ + int i, j; + int order, order2; + + j = 1 + (ptr->pawns[1] > 0); + order = data[0] & 0x0f; + order2 = ptr->pawns[1] ? (data[1] & 0x0f) : 0x0f; + for (i = 0; i < ptr->num; i++) + ptr->file[f].pieces[i] = data[i + j] & 0x0f; + set_norm_pawn((struct TBEntry_pawn *)ptr, ptr->file[f].norm, ptr->file[f].pieces); + tb_size[0] = calc_factors_pawn(ptr->file[f].factor, ptr->num, order, order2, ptr->file[f].norm, f); +} + +static void calc_symlen(struct PairsData *d, int s, char *tmp) +{ + int s1, s2; + + int w = *(int *)(d->sympat + 3 * s); + s2 = (w >> 12) & 0x0fff; + if (s2 == 0x0fff) + d->symlen[s] = 0; + else { + s1 = w & 0x0fff; + if (!tmp[s1]) calc_symlen(d, s1, tmp); + if (!tmp[s2]) calc_symlen(d, s2, tmp); + d->symlen[s] = d->symlen[s1] + d->symlen[s2] + 1; + } + tmp[s] = 1; +} + +static struct PairsData *setup_pairs(unsigned char *data, uint64_t tb_size, uint64_t *size, unsigned char **next, ubyte *flags, int wdl) +{ + struct PairsData *d; + int i; + + *flags = data[0]; + if (data[0] & 0x80) { + d = (struct PairsData *)malloc(sizeof(struct PairsData)); + d->idxbits = 0; + if (wdl) + d->min_len = data[1]; + else + d->min_len = 0; + *next = data + 2; + size[0] = size[1] = size[2] = 0; + return d; + } + + int blocksize = data[1]; + int idxbits = data[2]; + int real_num_blocks = *(uint32_t *)(&data[4]); + int num_blocks = real_num_blocks + *(ubyte *)(&data[3]); + int max_len = data[8]; + int min_len = data[9]; + int h = max_len - min_len + 1; + int num_syms = *(ushort *)(&data[10 + 2 * h]); + d = (struct PairsData *)malloc(sizeof(struct PairsData) + (h - 1) * sizeof(base_t) + num_syms); + d->blocksize = blocksize; + d->idxbits = idxbits; + d->offset = (ushort *)(&data[10]); + d->symlen = ((ubyte *)d) + sizeof(struct PairsData) + (h - 1) * sizeof(base_t); + d->sympat = &data[12 + 2 * h]; + d->min_len = min_len; + *next = &data[12 + 2 * h + 3 * num_syms + (num_syms & 1)]; + + int num_indices = (tb_size + (1ULL << idxbits) - 1) >> idxbits; + size[0] = 6ULL * num_indices; + size[1] = 2ULL * num_blocks; + size[2] = (1ULL << blocksize) * real_num_blocks; + + // char tmp[num_syms]; + char tmp[4096]; + for (i = 0; i < num_syms; i++) + tmp[i] = 0; + for (i = 0; i < num_syms; i++) + if (!tmp[i]) + calc_symlen(d, i, tmp); + + d->base[h - 1] = 0; + for (i = h - 2; i >= 0; i--) + d->base[i] = (d->base[i + 1] + d->offset[i] - d->offset[i + 1]) / 2; + for (i = 0; i < h; i++) + d->base[i] <<= 64 - (min_len + i); + + d->offset -= d->min_len; + + return d; +} + +static int init_table_wdl(struct TBEntry *entry, const char *str) +{ + ubyte *next; + int f, s; + uint64_t tb_size[8]; + uint64_t size[8 * 3]; + ubyte flags; + + // first mmap the table into memory + + entry->data = map_file(str, WDLSUFFIX, &entry->mapping); + if (!entry->data) { + std::cout << "Could not find " << str << WDLSUFFIX << std::endl; + return 0; + } + + ubyte *data = (ubyte *)entry->data; + if (((uint32_t *)data)[0] != WDL_MAGIC) { + std::cout << "Corrupted table" << std::endl; + unmap_file(entry->data, entry->mapping); + entry->data = 0; + return 0; + } + + int split = data[4] & 0x01; + int files = data[4] & 0x02 ? 4 : 1; + + data += 5; + + if (!entry->has_pawns) { + struct TBEntry_piece *ptr = (struct TBEntry_piece *)entry; + setup_pieces_piece(ptr, data, &tb_size[0]); + data += ptr->num + 1; + data += ((uintptr_t)data) & 0x01; + + ptr->precomp[0] = setup_pairs(data, tb_size[0], &size[0], &next, &flags, 1); + data = next; + if (split) { + ptr->precomp[1] = setup_pairs(data, tb_size[1], &size[3], &next, &flags, 1); + data = next; + } else + ptr->precomp[1] = NULL; + + ptr->precomp[0]->indextable = (char *)data; + data += size[0]; + if (split) { + ptr->precomp[1]->indextable = (char *)data; + data += size[3]; + } + + ptr->precomp[0]->sizetable = (ushort *)data; + data += size[1]; + if (split) { + ptr->precomp[1]->sizetable = (ushort *)data; + data += size[4]; + } + + data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f); + ptr->precomp[0]->data = data; + data += size[2]; + if (split) { + data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f); + ptr->precomp[1]->data = data; + } + } else { + struct TBEntry_pawn *ptr = (struct TBEntry_pawn *)entry; + s = 1 + (ptr->pawns[1] > 0); + for (f = 0; f < 4; f++) { + setup_pieces_pawn((struct TBEntry_pawn *)ptr, data, &tb_size[2 * f], f); + data += ptr->num + s; + } + data += ((uintptr_t)data) & 0x01; + + for (f = 0; f < files; f++) { + ptr->file[f].precomp[0] = setup_pairs(data, tb_size[2 * f], &size[6 * f], &next, &flags, 1); + data = next; + if (split) { + ptr->file[f].precomp[1] = setup_pairs(data, tb_size[2 * f + 1], &size[6 * f + 3], &next, &flags, 1); + data = next; + } else + ptr->file[f].precomp[1] = NULL; + } + + for (f = 0; f < files; f++) { + ptr->file[f].precomp[0]->indextable = (char *)data; + data += size[6 * f]; + if (split) { + ptr->file[f].precomp[1]->indextable = (char *)data; + data += size[6 * f + 3]; + } + } + + for (f = 0; f < files; f++) { + ptr->file[f].precomp[0]->sizetable = (ushort *)data; + data += size[6 * f + 1]; + if (split) { + ptr->file[f].precomp[1]->sizetable = (ushort *)data; + data += size[6 * f + 4]; + } + } + + for (f = 0; f < files; f++) { + data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f); + ptr->file[f].precomp[0]->data = data; + data += size[6 * f + 2]; + if (split) { + data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f); + ptr->file[f].precomp[1]->data = data; + data += size[6 * f + 5]; + } + } + } + + return 1; +} + +static int init_table_dtz(struct TBEntry *entry) +{ + ubyte *data = (ubyte *)entry->data; + ubyte *next; + int f, s; + uint64_t tb_size[4]; + uint64_t size[4 * 3]; + + if (!data) + return 0; + + if (((uint32_t *)data)[0] != DTZ_MAGIC) { + std::cout << "Corrupted table" << std::endl; + return 0; + } + + int files = data[4] & 0x02 ? 4 : 1; + + data += 5; + + if (!entry->has_pawns) { + struct DTZEntry_piece *ptr = (struct DTZEntry_piece *)entry; + setup_pieces_piece_dtz(ptr, data, &tb_size[0]); + data += ptr->num + 1; + data += ((uintptr_t)data) & 0x01; + + ptr->precomp = setup_pairs(data, tb_size[0], &size[0], &next, &(ptr->flags), 0); + data = next; + + ptr->map = data; + if (ptr->flags & 2) { + int i; + for (i = 0; i < 4; i++) { + ptr->map_idx[i] = (data + 1 - ptr->map); + data += 1 + data[0]; + } + data += ((uintptr_t)data) & 0x01; + } + + ptr->precomp->indextable = (char *)data; + data += size[0]; + + ptr->precomp->sizetable = (ushort *)data; + data += size[1]; + + data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f); + ptr->precomp->data = data; + data += size[2]; + } else { + struct DTZEntry_pawn *ptr = (struct DTZEntry_pawn *)entry; + s = 1 + (ptr->pawns[1] > 0); + for (f = 0; f < 4; f++) { + setup_pieces_pawn_dtz(ptr, data, &tb_size[f], f); + data += ptr->num + s; + } + data += ((uintptr_t)data) & 0x01; + + for (f = 0; f < files; f++) { + ptr->file[f].precomp = setup_pairs(data, tb_size[f], &size[3 * f], &next, &(ptr->flags[f]), 0); + data = next; + } + + ptr->map = data; + for (f = 0; f < files; f++) { + if (ptr->flags[f] & 2) { + int i; + for (i = 0; i < 4; i++) { + ptr->map_idx[f][i] = (data + 1 - ptr->map); + data += 1 + data[0]; + } + } + } + data += ((uintptr_t)data) & 0x01; + + for (f = 0; f < files; f++) { + ptr->file[f].precomp->indextable = (char *)data; + data += size[3 * f]; + } + + for (f = 0; f < files; f++) { + ptr->file[f].precomp->sizetable = (ushort *)data; + data += size[3 * f + 1]; + } + + for (f = 0; f < files; f++) { + data = (ubyte *)((((uintptr_t)data) + 0x3f) & ~0x3f); + ptr->file[f].precomp->data = data; + data += size[3 * f + 2]; + } + } + + return 1; +} + +static ubyte decompress_pairs(struct PairsData *d, uint64_t idx) +{ + if (!d->idxbits) + return d->min_len; + + uint32_t mainidx = idx >> d->idxbits; + int litidx = (idx & ((1 << d->idxbits) - 1)) - (1 << (d->idxbits - 1)); + uint32_t block = *(uint32_t *)(d->indextable + 6 * mainidx); + litidx += *(ushort *)(d->indextable + 6 * mainidx + 4); + if (litidx < 0) { + do { + litidx += d->sizetable[--block] + 1; + } while (litidx < 0); + } else { + while (litidx > d->sizetable[block]) + litidx -= d->sizetable[block++] + 1; + } + + uint32_t *ptr = (uint32_t *)(d->data + (block << d->blocksize)); + + int m = d->min_len; + ushort *offset = d->offset; + base_t *base = d->base - m; + ubyte *symlen = d->symlen; + int sym, bitcnt; + + uint64_t code = __builtin_bswap64(*((uint64_t *)ptr)); + ptr += 2; + bitcnt = 0; // number of "empty bits" in code + for (;;) { + int l = m; + while (code < base[l]) l++; + sym = offset[l] + ((code - base[l]) >> (64 - l)); + if (litidx < (int)symlen[sym] + 1) break; + litidx -= (int)symlen[sym] + 1; + code <<= l; + bitcnt += l; + if (bitcnt >= 32) { + bitcnt -= 32; + code |= ((uint64_t)(__builtin_bswap32(*ptr++))) << bitcnt; + } + } + + ubyte *sympat = d->sympat; + while (symlen[sym] != 0) { + int w = *(int *)(sympat + 3 * sym); + int s1 = w & 0x0fff; + if (litidx < (int)symlen[s1] + 1) + sym = s1; + else { + litidx -= (int)symlen[s1] + 1; + sym = (w >> 12) & 0x0fff; + } + } + + return *(sympat + 3 * sym); +} + +TBEntry* load_dtz_table(const char* str, uint64_t key1, uint64_t key2) +{ + int i; + struct TBEntry *ptr, *ptr3; + struct TBHashEntry *ptr2; + + // find corresponding WDL entry + ptr2 = WDL_hash[key1 >> (64 - TBHASHBITS)]; + for (i = 0; i < HSHMAX; i++) + if (ptr2[i].key == key1) break; + if (i == HSHMAX) return NULL; + ptr = ptr2[i].ptr; + + ptr3 = (struct TBEntry *)malloc(ptr->has_pawns + ? sizeof(struct DTZEntry_pawn) + : sizeof(struct DTZEntry_piece)); + + ptr3->data = map_file(str, DTZSUFFIX, &ptr3->mapping); + ptr3->key = ptr->key; + ptr3->num = ptr->num; + ptr3->symmetric = ptr->symmetric; + ptr3->has_pawns = ptr->has_pawns; + if (ptr3->has_pawns) { + struct DTZEntry_pawn *entry = (struct DTZEntry_pawn *)ptr3; + entry->pawns[0] = ((struct TBEntry_pawn *)ptr)->pawns[0]; + entry->pawns[1] = ((struct TBEntry_pawn *)ptr)->pawns[1]; + } else { + struct DTZEntry_piece *entry = (struct DTZEntry_piece *)ptr3; + entry->enc_type = ((struct TBEntry_piece *)ptr)->enc_type; + } + if (!init_table_dtz(ptr3)) { + free(ptr3); + return NULL; + } + return ptr3; +} + +static void free_wdl_entry(struct TBEntry *entry) +{ + unmap_file(entry->data, entry->mapping); + entry->data = NULL; + if (!entry->has_pawns) { + struct TBEntry_piece *ptr = (struct TBEntry_piece *)entry; + free(ptr->precomp[0]); ptr->precomp[0] = NULL; + free(ptr->precomp[1]); ptr->precomp[1] = NULL; + } else { + struct TBEntry_pawn *ptr = (struct TBEntry_pawn *)entry; + int f; + for (f = 0; f < 4; f++) { + free(ptr->file[f].precomp[0]); ptr->file[f].precomp[0] = NULL; + free(ptr->file[f].precomp[1]); ptr->file[f].precomp[1] = NULL; + } + } +} + +static void free_dtz_entry(struct TBEntry *entry) +{ + unmap_file(entry->data, entry->mapping); + entry->data = NULL; + if (!entry->has_pawns) { + struct DTZEntry_piece *ptr = (struct DTZEntry_piece *)entry; + free(ptr->precomp); ptr->precomp = NULL; + } else { + struct DTZEntry_pawn *ptr = (struct DTZEntry_pawn *)entry; + int f; + for (f = 0; f < 4; f++) { + free(ptr->file[f].precomp); ptr->file[f].precomp = NULL; + } + } + free(entry); +} + +static int wdl_to_map[5] = { 1, 3, 0, 2, 0 }; +static ubyte pa_flags[5] = { 8, 0, 0, 0, 4 }; + diff --git a/DroidFish/jni/stockfish/tbcore.h b/DroidFish/jni/stockfish/tbcore.h new file mode 100644 index 0000000..f9c2d56 --- /dev/null +++ b/DroidFish/jni/stockfish/tbcore.h @@ -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 +#define SEP_CHAR ';' +#define FD HANDLE +#define FD_ERR INVALID_HANDLE_VALUE +#endif + +#include +#include + +#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 ready; + ubyte num; + ubyte symmetric; + ubyte has_pawns; +} __attribute__((__may_alias__)); + +struct TBEntry_piece { + char *data; + uint64_t key; + uint64_t mapping; + std::atomic 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 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 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 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 entry; +}; + +#endif diff --git a/DroidFish/jni/stockfish/tbprobe.cpp b/DroidFish/jni/stockfish/tbprobe.cpp new file mode 100644 index 0000000..fa79e51 --- /dev/null +++ b/DroidFish/jni/stockfish/tbprobe.cpp @@ -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(pos.pieces(color, pt)); i > 0; i--) + *str++ = pchr[6 - pt]; + *str++ = 'v'; + color = ~color; + for (pt = KING; pt >= PAWN; --pt) + for (i = popcount(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(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(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 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 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(pos, stack); + // Since underpromotion captures are not included, we need to add them. + end = add_underprom_caps(pos, stack, end); + } else + end = generate(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(pos, stack); + else + end = generate(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(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(pos, stack); + else + end = generate(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(pos, stack); + else + end = generate(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(pos, stack); + else + end = generate(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(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(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; +} + diff --git a/DroidFish/jni/stockfish/tbprobe.h b/DroidFish/jni/stockfish/tbprobe.h new file mode 100644 index 0000000..9ed6f0a --- /dev/null +++ b/DroidFish/jni/stockfish/tbprobe.h @@ -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 diff --git a/DroidFish/jni/stockfish/thread.cpp b/DroidFish/jni/stockfish/thread.cpp index 344cfb7..3b98ac6 100644 --- a/DroidFish/jni/stockfish/thread.cpp +++ b/DroidFish/jni/stockfish/thread.cpp @@ -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 void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Value* bestValue, Move* bestMove, Depth depth, int moveCount, MovePicker* movePicker, int nodeType, bool cutNode) { @@ -297,14 +296,13 @@ void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Valu activeSplitPoint = &sp; activePosition = NULL; - if (!Fake) - for (Thread* slave; (slave = Threads.available_slave(this)) != NULL; ) - { - sp.slavesMask.set(slave->idx); - slave->activeSplitPoint = &sp; - slave->searching = true; // Slave leaves idle_loop() - slave->notify_one(); // Could be sleeping - } + for (Thread* slave; (slave = Threads.available_slave(this)) != NULL; ) + { + sp.slavesMask.set(slave->idx); + slave->activeSplitPoint = &sp; + slave->searching = true; // Slave leaves idle_loop() + slave->notify_one(); // Could be sleeping + } // Everything is set up. The master thread enters the idle loop, from which // it will instantly launch a search, because its 'searching' flag is set. @@ -339,11 +337,6 @@ void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Valu Threads.mutex.unlock(); } -// Explicit template instantiations -template void Thread::split(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() { diff --git a/DroidFish/jni/stockfish/thread.h b/DroidFish/jni/stockfish/thread.h index 3565431..26aed39 100644 --- a/DroidFish/jni/stockfish/thread.h +++ b/DroidFish/jni/stockfish/thread.h @@ -117,7 +117,6 @@ struct Thread : public ThreadBase { bool cutoff_occurred() const; bool available_to(const Thread* master) const; - template 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); diff --git a/DroidFish/jni/stockfish/timeman.cpp b/DroidFish/jni/stockfish/timeman.cpp index c305e61..88a52bb 100644 --- a/DroidFish/jni/stockfish/timeman.cpp +++ b/DroidFish/jni/stockfish/timeman.cpp @@ -78,7 +78,7 @@ void TimeManager::init(const Search::LimitsType& limits, int currentPly, Color u increment > 0 && movesToGo == 0 means: x basetime + z increment increment > 0 && movesToGo != 0 means: x moves in y minutes + z increment - Time management is adjusted by following UCI parameters: + Time management is adjusted by following parameters: emergencyMoveHorizon: Be prepared to always play at least this many moves emergencyBaseTime : Always attempt to keep at least this much time (in ms) at clock @@ -89,11 +89,9 @@ void TimeManager::init(const Search::LimitsType& limits, int currentPly, Color u int hypMTG, hypMyTime, t1, t2; // Read uci parameters - int emergencyMoveHorizon = Options["Emergency Move Horizon"]; - int emergencyBaseTime = Options["Emergency Base Time"]; - int emergencyMoveTime = Options["Emergency Move Time"]; - int minThinkingTime = Options["Minimum Thinking Time"]; - int slowMover = Options["Slow Mover"]; + int moveOverhead = Options["Move Overhead"]; + int minThinkingTime = Options["Minimum Thinking Time"]; + int slowMover = Options["Slow Mover"]; // Initialize unstablePvFactor to 1 and search times to maximum values unstablePvFactor = 1; @@ -106,8 +104,7 @@ void TimeManager::init(const Search::LimitsType& limits, int currentPly, Color u // Calculate thinking time for hypothetical "moves to go"-value hypMyTime = limits.time[us] + limits.inc[us] * (hypMTG - 1) - - emergencyBaseTime - - emergencyMoveTime * std::min(hypMTG, emergencyMoveHorizon); + - moveOverhead * (2 + std::min(hypMTG, 40)); hypMyTime = std::max(hypMyTime, 0); diff --git a/DroidFish/jni/stockfish/tt.cpp b/DroidFish/jni/stockfish/tt.cpp index 3d2fe48..46d891c 100644 --- a/DroidFish/jni/stockfish/tt.cpp +++ b/DroidFish/jni/stockfish/tt.cpp @@ -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); } diff --git a/DroidFish/jni/stockfish/tt.h b/DroidFish/jni/stockfish/tt.h index eeb18e3..534409f 100644 --- a/DroidFish/jni/stockfish/tt.h +++ b/DroidFish/jni/stockfish/tt.h @@ -23,70 +23,80 @@ #include "misc.h" #include "types.h" -/// The TTEntry is the 14 bytes transposition table entry, defined as below: +/// The TTEntry is the 10 bytes transposition table entry, defined as below: /// -/// key 32 bit +/// key 16 bit /// move 16 bit -/// bound type 8 bit -/// generation 8 bit /// value 16 bit -/// depth 16 bit /// eval value 16 bit +/// generation 6 bit +/// bound type 2 bit +/// depth 8 bit struct TTEntry { Move move() const { return (Move )move16; } - Bound bound() const { return (Bound)bound8; } Value value() const { return (Value)value16; } - Depth depth() const { return (Depth)depth16; } Value eval_value() const { return (Value)evalValue; } + Depth depth() const { return (Depth)depth8; } + Bound bound() const { return (Bound)(genBound8 & 0x3); } private: friend class TranspositionTable; - void save(uint32_t k, Value v, Bound b, Depth d, Move m, uint8_t g, Value ev) { + void save(uint16_t k, Value v, Bound b, Depth d, Move m, uint8_t g, Value ev) { - key32 = (uint32_t)k; - move16 = (uint16_t)m; - bound8 = (uint8_t)b; - generation8 = (uint8_t)g; - value16 = (int16_t)v; - depth16 = (int16_t)d; - evalValue = (int16_t)ev; + key16 = (uint16_t)k; + move16 = (uint16_t)m; + value16 = (int16_t)v; + evalValue = (int16_t)ev; + genBound8 = (uint8_t)(g | b); + depth8 = (int8_t)d; } - uint32_t key32; + uint16_t key16; uint16_t move16; - uint8_t bound8, generation8; - int16_t value16, depth16, evalValue; + int16_t value16; + int16_t evalValue; + uint8_t genBound8; + int8_t depth8; }; +/// TTCluster is a 32 bytes cluster of TT entries consisting of: +/// +/// 3 x TTEntry (3 x 10 bytes) +/// padding (2 bytes) + +const unsigned TTClusterSize = 3; + +struct TTCluster { + TTEntry entry[TTClusterSize]; + char padding[2]; +}; /// A TranspositionTable consists of a power of 2 number of clusters and each -/// cluster consists of ClusterSize number of TTEntry. Each non-empty entry +/// cluster consists of TTClusterSize number of TTEntry. Each non-empty entry /// contains information of exactly one position. The size of a cluster should /// not be bigger than a cache line size. In case it is less, it should be padded /// to guarantee always aligned accesses. class TranspositionTable { - static const unsigned ClusterSize = 4; - public: ~TranspositionTable() { free(mem); } - void new_search() { ++generation; } + void new_search() { generation += 4; } // Lower 2 bits are used by Bound const TTEntry* probe(const Key key) const; TTEntry* first_entry(const Key key) const; - void resize(uint64_t mbSize); + void resize(size_t mbSize); void clear(); void store(const Key key, Value v, Bound type, Depth d, Move m, Value statV); private: - uint32_t hashMask; - TTEntry* table; + size_t clusterCount; + TTCluster* table; void* mem; - uint8_t generation; // Size must be not bigger than TTEntry::generation8 + uint8_t generation; // Size must be not bigger than TTEntry::genBound8 }; extern TranspositionTable TT; @@ -94,11 +104,11 @@ extern TranspositionTable TT; /// TranspositionTable::first_entry() returns a pointer to the first entry of /// a cluster given a position. The lowest order bits of the key are used to -/// get the index of the cluster. +/// get the index of the cluster inside the table. inline TTEntry* TranspositionTable::first_entry(const Key key) const { - return table + ((uint32_t)key & hashMask); + return &table[(size_t)key & (clusterCount - 1)].entry[0]; } #endif // #ifndef TT_H_INCLUDED diff --git a/DroidFish/jni/stockfish/types.h b/DroidFish/jni/stockfish/types.h index 9910f10..de0723f 100644 --- a/DroidFish/jni/stockfish/types.h +++ b/DroidFish/jni/stockfish/types.h @@ -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 - -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 diff --git a/DroidFish/jni/stockfish/uci.cpp b/DroidFish/jni/stockfish/uci.cpp index 644fc47..6027295 100644 --- a/DroidFish/jni/stockfish/uci.cpp +++ b/DroidFish/jni/stockfish/uci.cpp @@ -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; diff --git a/DroidFish/jni/stockfish/ucioption.cpp b/DroidFish/jni/stockfish/ucioption.cpp index d5db2c9..6ebf8d7 100644 --- a/DroidFish/jni/stockfish/ucioption.cpp +++ b/DroidFish/jni/stockfish/ucioption.cpp @@ -27,6 +27,7 @@ #include "thread.h" #include "tt.h" #include "ucioption.h" +#include "tbprobe.h" using std::string; @@ -40,6 +41,7 @@ void on_eval(const Option&) { Eval::init(); } void on_threads(const Option&) { Threads.read_uci_options(); } void on_hash_size(const Option& o) { TT.resize(o); } void on_clear_hash(const Option&) { TT.clear(); } +void on_tb_path(const Option& o) { Tablebases::init(o); } /// Our case insensitive less() function as required by UCI protocol @@ -54,35 +56,23 @@ bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const void init(OptionsMap& o) { - o["Write Debug Log"] << Option(false, on_logger); - o["Write Search Log"] << Option(false); - o["Search Log Filename"] << Option("SearchLog.txt"); - o["Book File"] << Option("book.bin"); - o["Best Book Move"] << Option(false); - o["Contempt Factor"] << Option(0, -50, 50); - o["Mobility (Midgame)"] << Option(100, 0, 200, on_eval); - o["Mobility (Endgame)"] << Option(100, 0, 200, on_eval); - o["Pawn Structure (Midgame)"] << Option(100, 0, 200, on_eval); - o["Pawn Structure (Endgame)"] << Option(100, 0, 200, on_eval); - o["Passed Pawns (Midgame)"] << Option(100, 0, 200, on_eval); - o["Passed Pawns (Endgame)"] << Option(100, 0, 200, on_eval); - o["Space"] << Option(100, 0, 200, on_eval); - o["Aggressiveness"] << Option(100, 0, 200, on_eval); - o["Cowardice"] << Option(100, 0, 200, on_eval); - o["Min Split Depth"] << Option(0, 0, 12, on_threads); - o["Threads"] << Option(1, 1, MAX_THREADS, on_threads); - o["Hash"] << Option(32, 1, 16384, on_hash_size); - o["Clear Hash"] << Option(on_clear_hash); - o["Ponder"] << Option(true); - o["OwnBook"] << Option(false); - o["MultiPV"] << Option(1, 1, 500); - o["Skill Level"] << Option(20, 0, 20); - o["Emergency Move Horizon"] << Option(40, 0, 50); - o["Emergency Base Time"] << Option(60, 0, 30000); - o["Emergency Move Time"] << Option(30, 0, 5000); - o["Minimum Thinking Time"] << Option(20, 0, 5000); - o["Slow Mover"] << Option(80, 10, 1000); - o["UCI_Chess960"] << Option(false); + o["Write Debug Log"] << Option(false, on_logger); + o["Contempt"] << Option(0, -100, 100); + o["Min Split Depth"] << Option(0, 0, 12, on_threads); + o["Threads"] << Option(1, 1, MAX_THREADS, on_threads); + o["Hash"] << Option(16, 1, 1024 * 1024, on_hash_size); + o["Clear Hash"] << Option(on_clear_hash); + o["Ponder"] << Option(true); + o["MultiPV"] << Option(1, 1, 500); + o["Skill Level"] << Option(20, 0, 20); + o["Move Overhead"] << Option(30, 0, 5000); + o["Minimum Thinking Time"] << Option(20, 0, 5000); + o["Slow Mover"] << Option(80, 10, 1000); + o["UCI_Chess960"] << Option(false); + o["SyzygyPath"] << Option("", on_tb_path); + o["SyzygyProbeDepth"] << Option(1, 1, 100); + o["Syzygy50MoveRule"] << Option(true); + o["SyzygyProbeLimit"] << Option(6, 0, 6); }