diff --git a/DroidFish/jni/stockfish/Android.mk b/DroidFish/jni/stockfish/Android.mk index 776b7a4..dbb0932 100644 --- a/DroidFish/jni/stockfish/Android.mk +++ b/DroidFish/jni/stockfish/Android.mk @@ -1,9 +1,9 @@ LOCAL_PATH := $(call my-dir) SF_SRC_FILES := \ - 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 tbprobe.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 search.cpp tt.cpp syzygy/tbprobe.cpp include $(CLEAR_VARS) LOCAL_MODULE := stockfish-nopie diff --git a/DroidFish/jni/stockfish/benchmark.cpp b/DroidFish/jni/stockfish/benchmark.cpp index 3161c4c..605c95a 100644 --- a/DroidFish/jni/stockfish/benchmark.cpp +++ b/DroidFish/jni/stockfish/benchmark.cpp @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -24,16 +24,17 @@ #include #include "misc.h" -#include "notation.h" #include "position.h" #include "search.h" #include "thread.h" #include "tt.h" -#include "ucioption.h" +#include "uci.h" using namespace std; -static const char* Defaults[] = { +namespace { + +const char* Defaults[] = { "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 10", "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 11", @@ -63,9 +64,23 @@ static const char* Defaults[] = { "6k1/6p1/P6p/r1N5/5p2/7P/1b3PP1/4R1K1 w - - 0 1", "1r3k2/4q3/2Pp3b/3Bp3/2Q2p2/1p1P2P1/1P2KP2/3N4 w - - 0 1", "6k1/4pp1p/3p2p1/P1pPb3/R7/1r2P1PP/3B1P2/6K1 w - - 0 1", - "8/3p3B/5p2/5P2/p7/PP5b/k7/6K1 w - - 0 1" + "8/3p3B/5p2/5P2/p7/PP5b/k7/6K1 w - - 0 1", + + // 5-man positions + "8/8/8/8/5kp1/P7/8/1K1N4 w - - 0 1", // Kc2 - mate + "8/8/8/5N2/8/p7/8/2NK3k w - - 0 1", // Na2 - mate + "8/3k4/8/8/8/4B3/4KB2/2B5 w - - 0 1", // draw + + // 6-man positions + "8/8/1P6/5pr1/8/4R3/7k/2K5 w - - 0 1", // Re5 - mate + "8/2p4P/8/kr6/6R1/8/8/1K6 w - - 0 1", // Ka2 - mate + "8/8/3P3k/8/1p6/8/1P6/1K3n2 b - - 0 1", // Nd2 - draw + + // 7-man positions + "8/R7/2q5/8/6k1/8/1P5p/K6R w - - 0 124", // Draw }; +} // namespace /// benchmark() runs a simple benchmark by letting Stockfish analyze a set /// of positions for a given limit each. There are five parameters: the @@ -73,7 +88,7 @@ static const char* Defaults[] = { /// be used, the limit value spent for each position (optional, default is /// depth 13), an optional file name where to look for positions in FEN /// format (defaults are the positions defined above) and the type of the -/// limit value: depth (default), time in secs or number of nodes. +/// limit value: depth (default), time in millisecs or number of nodes. void benchmark(const Position& current, istream& is) { @@ -93,7 +108,7 @@ void benchmark(const Position& current, istream& is) { TT.clear(); if (limitType == "time") - limits.movetime = 1000 * atoi(limit.c_str()); // movetime is in ms + limits.movetime = atoi(limit.c_str()); // movetime is in ms else if (limitType == "nodes") limits.nodes = atoi(limit.c_str()); @@ -105,7 +120,7 @@ void benchmark(const Position& current, istream& is) { limits.depth = atoi(limit.c_str()); if (fenFile == "default") - fens.assign(Defaults, Defaults + 30); + fens.assign(Defaults, Defaults + 37); else if (fenFile == "current") fens.push_back(current.fen()); diff --git a/DroidFish/jni/stockfish/bitbase.cpp b/DroidFish/jni/stockfish/bitbase.cpp index 0f274c8..a018d3c 100644 --- a/DroidFish/jni/stockfish/bitbase.cpp +++ b/DroidFish/jni/stockfish/bitbase.cpp @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -41,7 +41,7 @@ namespace { // bit 13-14: white pawn file (from FILE_A to FILE_D) // bit 15-17: white pawn RANK_7 - rank (from RANK_7 - RANK_7 to RANK_7 - RANK_2) unsigned index(Color us, Square bksq, Square wksq, Square psq) { - return wksq + (bksq << 6) + (us << 12) + (file_of(psq) << 13) + ((RANK_7 - rank_of(psq)) << 15); + return wksq | (bksq << 6) | (us << 12) | (file_of(psq) << 13) | ((RANK_7 - rank_of(psq)) << 15); } enum Result { @@ -71,7 +71,7 @@ namespace { } // namespace -bool Bitbases::probe_kpk(Square wksq, Square wpsq, Square bksq, Color us) { +bool Bitbases::probe(Square wksq, Square wpsq, Square bksq, Color us) { assert(file_of(wpsq) <= FILE_D); @@ -80,7 +80,7 @@ bool Bitbases::probe_kpk(Square wksq, Square wpsq, Square bksq, Color us) { } -void Bitbases::init_kpk() { +void Bitbases::init() { unsigned idx, repeat = 1; std::vector db; @@ -114,7 +114,7 @@ namespace { result = UNKNOWN; // Check if two pieces are on the same square or if a king can be captured - if ( square_distance(wksq, bksq) <= 1 + if ( distance(wksq, bksq) <= 1 || wksq == psq || bksq == psq || (us == WHITE && (StepAttacksBB[PAWN][psq] & bksq))) @@ -125,7 +125,7 @@ namespace { // Immediate win if a pawn can be promoted without getting captured if ( rank_of(psq) == RANK_7 && wksq != psq + DELTA_N - && ( square_distance(bksq, psq + DELTA_N) > 1 + && ( distance(bksq, psq + DELTA_N) > 1 ||(StepAttacksBB[KING][wksq] & (psq + DELTA_N)))) result = WIN; } diff --git a/DroidFish/jni/stockfish/bitboard.cpp b/DroidFish/jni/stockfish/bitboard.cpp index 844e1d0..32efaed 100644 --- a/DroidFish/jni/stockfish/bitboard.cpp +++ b/DroidFish/jni/stockfish/bitboard.cpp @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -18,23 +18,23 @@ */ #include -#include // For memset +#include // For std::memset #include "bitboard.h" #include "bitcount.h" -#include "rkiss.h" +#include "misc.h" -CACHE_LINE_ALIGNMENT +int SquareDistance[SQUARE_NB][SQUARE_NB]; -Bitboard RMasks[SQUARE_NB]; -Bitboard RMagics[SQUARE_NB]; -Bitboard* RAttacks[SQUARE_NB]; -unsigned RShifts[SQUARE_NB]; +Bitboard RookMasks [SQUARE_NB]; +Bitboard RookMagics [SQUARE_NB]; +Bitboard* RookAttacks[SQUARE_NB]; +unsigned RookShifts [SQUARE_NB]; -Bitboard BMasks[SQUARE_NB]; -Bitboard BMagics[SQUARE_NB]; -Bitboard* BAttacks[SQUARE_NB]; -unsigned BShifts[SQUARE_NB]; +Bitboard BishopMasks [SQUARE_NB]; +Bitboard BishopMagics [SQUARE_NB]; +Bitboard* BishopAttacks[SQUARE_NB]; +unsigned BishopShifts [SQUARE_NB]; Bitboard SquareBB[SQUARE_NB]; Bitboard FileBB[FILE_NB]; @@ -44,53 +44,44 @@ Bitboard InFrontBB[COLOR_NB][RANK_NB]; Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB]; Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; Bitboard LineBB[SQUARE_NB][SQUARE_NB]; -Bitboard DistanceRingsBB[SQUARE_NB][8]; +Bitboard DistanceRingBB[SQUARE_NB][8]; Bitboard ForwardBB[COLOR_NB][SQUARE_NB]; Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB]; Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB]; Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; -int SquareDistance[SQUARE_NB][SQUARE_NB]; - namespace { // De Bruijn sequences. See chessprogramming.wikispaces.com/BitScan - const uint64_t DeBruijn_64 = 0x3F79D71B4CB0A89ULL; - const uint32_t DeBruijn_32 = 0x783A9B23; + const uint64_t DeBruijn64 = 0x3F79D71B4CB0A89ULL; + const uint32_t DeBruijn32 = 0x783A9B23; - CACHE_LINE_ALIGNMENT - - int MS1BTable[256]; - Square BSFTable[SQUARE_NB]; - Bitboard RTable[0x19000]; // Storage space for rook attacks - Bitboard BTable[0x1480]; // Storage space for bishop attacks + int MS1BTable[256]; // To implement software msb() + Square BSFTable[SQUARE_NB]; // To implement software bitscan + Bitboard RookTable[0x19000]; // To store rook attacks + Bitboard BishopTable[0x1480]; // To store bishop attacks typedef unsigned (Fn)(Square, Bitboard); void init_magics(Bitboard table[], Bitboard* attacks[], Bitboard magics[], Bitboard masks[], unsigned shifts[], Square deltas[], Fn index); - FORCE_INLINE unsigned bsf_index(Bitboard b) { + // bsf_index() returns the index into BSFTable[] to look up the bitscan. Uses + // Matt Taylor's folding for 32 bit case, extended to 64 bit by Kim Walisch. - // Matt Taylor's folding for 32 bit systems, extended to 64 bits by Kim Walisch - b ^= (b - 1); - return Is64Bit ? (b * DeBruijn_64) >> 58 - : ((unsigned(b) ^ unsigned(b >> 32)) * DeBruijn_32) >> 26; + FORCE_INLINE unsigned bsf_index(Bitboard b) { + b ^= b - 1; + return Is64Bit ? (b * DeBruijn64) >> 58 + : ((unsigned(b) ^ unsigned(b >> 32)) * DeBruijn32) >> 26; } } -/// lsb()/msb() finds the least/most significant bit in a non-zero bitboard. -/// pop_lsb() finds and clears the least significant bit in a non-zero bitboard. - #ifndef USE_BSFQ -Square lsb(Bitboard b) { return BSFTable[bsf_index(b)]; } +/// Software fall-back of lsb() and msb() for CPU lacking hardware support -Square pop_lsb(Bitboard* b) { - - Bitboard bb = *b; - *b = bb & (bb - 1); - return BSFTable[bsf_index(bb)]; +Square lsb(Bitboard b) { + return BSFTable[bsf_index(b)]; } Square msb(Bitboard b) { @@ -124,8 +115,8 @@ Square msb(Bitboard b) { #endif // ifndef USE_BSFQ -/// Bitboards::pretty() returns an ASCII representation of a bitboard to be -/// printed to standard output. This is sometimes useful for debugging. +/// Bitboards::pretty() returns an ASCII representation of a bitboard suitable +/// to be printed to standard output. Useful for debugging. const std::string Bitboards::pretty(Bitboard b) { @@ -181,8 +172,8 @@ void Bitboards::init() { for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) if (s1 != s2) { - SquareDistance[s1][s2] = std::max(file_distance(s1, s2), rank_distance(s1, s2)); - DistanceRingsBB[s1][SquareDistance[s1][s2] - 1] |= s2; + SquareDistance[s1][s2] = std::max(distance(s1, s2), distance(s1, s2)); + DistanceRingBB[s1][SquareDistance[s1][s2] - 1] |= s2; } int steps[][9] = { {}, { 7, 9 }, { 17, 15, 10, 6, -6, -10, -15, -17 }, @@ -195,15 +186,15 @@ void Bitboards::init() { { Square to = s + Square(c == WHITE ? steps[pt][i] : -steps[pt][i]); - if (is_ok(to) && square_distance(s, to) < 3) + if (is_ok(to) && distance(s, to) < 3) StepAttacksBB[make_piece(c, pt)][s] |= to; } - Square RDeltas[] = { DELTA_N, DELTA_E, DELTA_S, DELTA_W }; - Square BDeltas[] = { DELTA_NE, DELTA_SE, DELTA_SW, DELTA_NW }; + Square RookDeltas[] = { DELTA_N, DELTA_E, DELTA_S, DELTA_W }; + Square BishopDeltas[] = { DELTA_NE, DELTA_SE, DELTA_SW, DELTA_NW }; - init_magics(RTable, RAttacks, RMagics, RMasks, RShifts, RDeltas, magic_index); - init_magics(BTable, BAttacks, BMagics, BMasks, BShifts, BDeltas, magic_index); + init_magics(RookTable, RookAttacks, RookMagics, RookMasks, RookShifts, RookDeltas, magic_index); + init_magics(BishopTable, BishopAttacks, BishopMagics, BishopMasks, BishopShifts, BishopDeltas, magic_index); for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) { @@ -233,7 +224,7 @@ namespace { for (int i = 0; i < 4; ++i) for (Square s = sq + deltas[i]; - is_ok(s) && square_distance(s, s - deltas[i]) == 1; + is_ok(s) && distance(s, s - deltas[i]) == 1; s += deltas[i]) { attack |= s; @@ -254,11 +245,11 @@ namespace { void init_magics(Bitboard table[], Bitboard* attacks[], Bitboard magics[], Bitboard masks[], unsigned shifts[], Square deltas[], Fn index) { - int MagicBoosters[][RANK_NB] = { { 969, 1976, 2850, 542, 2069, 2852, 1708, 164 }, - { 3101, 552, 3555, 926, 834, 26, 2131, 1117 } }; - RKISS rk; + int seeds[][RANK_NB] = { { 8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020 }, + { 728, 10316, 55013, 32803, 12281, 15100, 16645, 255 } }; + Bitboard occupancy[4096], reference[4096], edges, b; - int i, size, booster; + int i, size; // attacks[s] is a pointer to the beginning of the attacks table for square 's' attacks[SQ_A1] = table; @@ -283,8 +274,8 @@ namespace { occupancy[size] = b; reference[size] = sliding_attack(deltas, s, b); - // if (HasPext) - // attacks[s][_pext_u64(b, masks[s])] = reference[size]; + if (HasPext) + attacks[s][pext(b, masks[s])] = reference[size]; size++; b = (b - masks[s]) & masks[s]; @@ -298,13 +289,13 @@ namespace { if (HasPext) continue; - booster = MagicBoosters[Is64Bit][rank_of(s)]; + PRNG rng(seeds[Is64Bit][rank_of(s)]); // 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); + magics[s] = rng.sparse_rand(); 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 3a48c7f..aa4e171 100644 --- a/DroidFish/jni/stockfish/bitboard.h +++ b/DroidFish/jni/stockfish/bitboard.h @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -25,6 +25,13 @@ #include "types.h" +namespace Bitbases { + +void init(); +bool probe(Square wksq, Square wpsq, Square bksq, Color us); + +} + namespace Bitboards { void init(); @@ -32,12 +39,7 @@ const std::string pretty(Bitboard b); } -namespace Bitbases { - -void init_kpk(); -bool probe_kpk(Square wksq, Square wpsq, Square bksq, Color us); - -} +const Bitboard DarkSquares = 0xAA55AA55AA55AA55ULL; const Bitboard FileABB = 0x0101010101010101ULL; const Bitboard FileBBB = FileABB << 1; @@ -57,17 +59,17 @@ const Bitboard Rank6BB = Rank1BB << (8 * 5); const Bitboard Rank7BB = Rank1BB << (8 * 6); const Bitboard Rank8BB = Rank1BB << (8 * 7); -CACHE_LINE_ALIGNMENT +extern int SquareDistance[SQUARE_NB][SQUARE_NB]; -extern Bitboard RMasks[SQUARE_NB]; -extern Bitboard RMagics[SQUARE_NB]; -extern Bitboard* RAttacks[SQUARE_NB]; -extern unsigned RShifts[SQUARE_NB]; +extern Bitboard RookMasks [SQUARE_NB]; +extern Bitboard RookMagics [SQUARE_NB]; +extern Bitboard* RookAttacks[SQUARE_NB]; +extern unsigned RookShifts [SQUARE_NB]; -extern Bitboard BMasks[SQUARE_NB]; -extern Bitboard BMagics[SQUARE_NB]; -extern Bitboard* BAttacks[SQUARE_NB]; -extern unsigned BShifts[SQUARE_NB]; +extern Bitboard BishopMasks [SQUARE_NB]; +extern Bitboard BishopMagics [SQUARE_NB]; +extern Bitboard* BishopAttacks[SQUARE_NB]; +extern unsigned BishopShifts [SQUARE_NB]; extern Bitboard SquareBB[SQUARE_NB]; extern Bitboard FileBB[FILE_NB]; @@ -77,15 +79,12 @@ extern Bitboard InFrontBB[COLOR_NB][RANK_NB]; extern Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB]; extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; extern Bitboard LineBB[SQUARE_NB][SQUARE_NB]; -extern Bitboard DistanceRingsBB[SQUARE_NB][8]; +extern Bitboard DistanceRingBB[SQUARE_NB][8]; extern Bitboard ForwardBB[COLOR_NB][SQUARE_NB]; extern Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB]; extern Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB]; extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; -extern int SquareDistance[SQUARE_NB][SQUARE_NB]; - -const Bitboard DarkSquares = 0xAA55AA55AA55AA55ULL; /// Overloads of bitwise operators between a Bitboard and a Square for testing /// whether a given bit is set in a bitboard, and for setting and clearing bits. @@ -94,14 +93,6 @@ inline Bitboard operator&(Bitboard b, Square s) { return b & SquareBB[s]; } -inline Bitboard& operator|=(Bitboard& b, Square s) { - return b |= SquareBB[s]; -} - -inline Bitboard& operator^=(Bitboard& b, Square s) { - return b ^= SquareBB[s]; -} - inline Bitboard operator|(Bitboard b, Square s) { return b | SquareBB[s]; } @@ -110,37 +101,21 @@ inline Bitboard operator^(Bitboard b, Square s) { return b ^ SquareBB[s]; } +inline Bitboard& operator|=(Bitboard& b, Square s) { + return b |= SquareBB[s]; +} + +inline Bitboard& operator^=(Bitboard& b, Square s) { + return b ^= SquareBB[s]; +} + inline bool more_than_one(Bitboard b) { return b & (b - 1); } -inline int square_distance(Square s1, Square s2) { - return SquareDistance[s1][s2]; -} -inline int file_distance(Square s1, Square s2) { - return abs(file_of(s1) - file_of(s2)); -} - -inline int rank_distance(Square s1, Square s2) { - return abs(rank_of(s1) - rank_of(s2)); -} - - -/// shift_bb() moves bitboard one step along direction Delta. Mainly for pawns. - -template -inline Bitboard shift_bb(Bitboard b) { - - return Delta == DELTA_N ? b << 8 : Delta == DELTA_S ? b >> 8 - : Delta == DELTA_NE ? (b & ~FileHBB) << 9 : Delta == DELTA_SE ? (b & ~FileHBB) >> 7 - : Delta == DELTA_NW ? (b & ~FileABB) << 7 : Delta == DELTA_SW ? (b & ~FileABB) >> 9 - : 0; -} - - -/// rank_bb() and file_bb() take a file or a square as input and return -/// a bitboard representing all squares on the given file or rank. +/// rank_bb() and file_bb() return a bitboard representing all the squares on +/// the given file or rank. inline Bitboard rank_bb(Rank r) { return RankBB[r]; @@ -159,119 +134,138 @@ inline Bitboard file_bb(Square s) { } -/// adjacent_files_bb() takes a file as input and returns a bitboard representing -/// all squares on the adjacent files. +/// shift_bb() moves a bitboard one step along direction Delta. Mainly for pawns + +template +inline Bitboard shift_bb(Bitboard b) { + return Delta == DELTA_N ? b << 8 : Delta == DELTA_S ? b >> 8 + : Delta == DELTA_NE ? (b & ~FileHBB) << 9 : Delta == DELTA_SE ? (b & ~FileHBB) >> 7 + : Delta == DELTA_NW ? (b & ~FileABB) << 7 : Delta == DELTA_SW ? (b & ~FileABB) >> 9 + : 0; +} + + +/// adjacent_files_bb() returns a bitboard representing all the squares on the +/// adjacent files of the given one. inline Bitboard adjacent_files_bb(File f) { return AdjacentFilesBB[f]; } -/// in_front_bb() takes a color and a rank as input, and returns a bitboard -/// representing all the squares on all ranks in front of the rank, from the -/// given color's point of view. For instance, in_front_bb(BLACK, RANK_3) will -/// give all squares on ranks 1 and 2. - -inline Bitboard in_front_bb(Color c, Rank r) { - return InFrontBB[c][r]; -} - - -/// between_bb() returns a bitboard representing all squares between two squares. -/// For instance, between_bb(SQ_C4, SQ_F7) returns a bitboard with the bits for -/// square d5 and e6 set. If s1 and s2 are not on the same rank, file or diagonal, -/// 0 is returned. +/// between_bb() returns a bitboard representing all the squares between the two +/// given ones. For instance, between_bb(SQ_C4, SQ_F7) returns a bitboard with +/// the bits for square d5 and e6 set. If s1 and s2 are not on the same rank, file +/// or diagonal, 0 is returned. inline Bitboard between_bb(Square s1, Square s2) { return BetweenBB[s1][s2]; } -/// forward_bb() takes a color and a square as input, and returns a bitboard -/// representing all squares along the line in front of the square, from the -/// point of view of the given color. Definition of the table is: -/// ForwardBB[c][s] = in_front_bb(c, s) & file_bb(s) +/// in_front_bb() returns a bitboard representing all the squares on all the ranks +/// in front of the given one, from the point of view of the given color. For +/// instance, in_front_bb(BLACK, RANK_3) will return the squares on ranks 1 and 2. + +inline Bitboard in_front_bb(Color c, Rank r) { + return InFrontBB[c][r]; +} + + +/// forward_bb() returns a bitboard representing all the squares along the line +/// in front of the given one, from the point of view of the given color: +/// ForwardBB[c][s] = in_front_bb(c, s) & file_bb(s) inline Bitboard forward_bb(Color c, Square s) { return ForwardBB[c][s]; } -/// pawn_attack_span() takes a color and a square as input, and returns a bitboard -/// representing all squares that can be attacked by a pawn of the given color -/// when it moves along its file starting from the given square. Definition is: -/// PawnAttackSpan[c][s] = in_front_bb(c, s) & adjacent_files_bb(s); +/// pawn_attack_span() returns a bitboard representing all the squares that can be +/// attacked by a pawn of the given color when it moves along its file, starting +/// from the given square: +/// PawnAttackSpan[c][s] = in_front_bb(c, s) & adjacent_files_bb(s); inline Bitboard pawn_attack_span(Color c, Square s) { return PawnAttackSpan[c][s]; } -/// passed_pawn_mask() takes a color and a square as input, and returns a -/// bitboard mask which can be used to test if a pawn of the given color on -/// the given square is a passed pawn. Definition of the table is: -/// PassedPawnMask[c][s] = pawn_attack_span(c, s) | forward_bb(c, s) +/// passed_pawn_mask() returns a bitboard mask which can be used to test if a +/// pawn of the given color and on the given square is a passed pawn: +/// PassedPawnMask[c][s] = pawn_attack_span(c, s) | forward_bb(c, s) inline Bitboard passed_pawn_mask(Color c, Square s) { return PassedPawnMask[c][s]; } -/// squares_of_color() returns a bitboard representing all squares with the same -/// color of the given square. +/// squares_of_color() returns a bitboard representing all the squares of the +/// same color of the given one. inline Bitboard squares_of_color(Square s) { return DarkSquares & s ? DarkSquares : ~DarkSquares; } -/// aligned() returns true if the squares s1, s2 and s3 are aligned -/// either on a straight or on a diagonal line. +/// aligned() returns true if the squares s1, s2 and s3 are aligned either on a +/// straight or on a diagonal line. inline bool aligned(Square s1, Square s2, Square s3) { return LineBB[s1][s2] & s3; } -/// Functions for computing sliding attack bitboards. Function attacks_bb() takes -/// a square and a bitboard of occupied squares as input, and returns a bitboard -/// representing all squares attacked by Pt (bishop or rook) on the given square. +/// distance() functions return the distance between x and y, defined as the +/// number of steps for a king in x to reach y. Works with squares, ranks, files. + +template inline int distance(T x, T y) { return x < y ? y - x : x - y; } +template<> inline int distance(Square x, Square y) { return SquareDistance[x][y]; } + +template inline int distance(T2 x, T2 y); +template<> inline int distance(Square x, Square y) { return distance(file_of(x), file_of(y)); } +template<> inline int distance(Square x, Square y) { return distance(rank_of(x), rank_of(y)); } + + +/// attacks_bb() returns a bitboard representing all the squares attacked by a +/// piece of type Pt (bishop or rook) placed on 's'. The helper magic_index() +/// looks up the index using the 'magic bitboards' approach. template -FORCE_INLINE unsigned magic_index(Square s, Bitboard occ) { +FORCE_INLINE unsigned magic_index(Square s, Bitboard occupied) { - Bitboard* const Masks = Pt == ROOK ? RMasks : BMasks; - Bitboard* const Magics = Pt == ROOK ? RMagics : BMagics; - unsigned* const Shifts = Pt == ROOK ? RShifts : BShifts; + Bitboard* const Masks = Pt == ROOK ? RookMasks : BishopMasks; + Bitboard* const Magics = Pt == ROOK ? RookMagics : BishopMagics; + unsigned* const Shifts = Pt == ROOK ? RookShifts : BishopShifts; - // if (HasPext) - // return unsigned(_pext_u64(occ, Masks[s])); + if (HasPext) + return unsigned(pext(occupied, Masks[s])); if (Is64Bit) - return unsigned(((occ & Masks[s]) * Magics[s]) >> Shifts[s]); + return unsigned(((occupied & Masks[s]) * Magics[s]) >> Shifts[s]); - unsigned lo = unsigned(occ) & unsigned(Masks[s]); - unsigned hi = unsigned(occ >> 32) & unsigned(Masks[s] >> 32); + unsigned lo = unsigned(occupied) & unsigned(Masks[s]); + unsigned hi = unsigned(occupied >> 32) & unsigned(Masks[s] >> 32); return (lo * unsigned(Magics[s]) ^ hi * unsigned(Magics[s] >> 32)) >> Shifts[s]; } template -inline Bitboard attacks_bb(Square s, Bitboard occ) { - return (Pt == ROOK ? RAttacks : BAttacks)[s][magic_index(s, occ)]; +inline Bitboard attacks_bb(Square s, Bitboard occupied) { + return (Pt == ROOK ? RookAttacks : BishopAttacks)[s][magic_index(s, occupied)]; } -inline Bitboard attacks_bb(Piece pc, Square s, Bitboard occ) { +inline Bitboard attacks_bb(Piece pc, Square s, Bitboard occupied) { switch (type_of(pc)) { - case BISHOP: return attacks_bb(s, occ); - case ROOK : return attacks_bb(s, occ); - case QUEEN : return attacks_bb(s, occ) | attacks_bb(s, occ); + case BISHOP: return attacks_bb(s, occupied); + case ROOK : return attacks_bb(s, occupied); + case QUEEN : return attacks_bb(s, occupied) | attacks_bb(s, occupied); default : return StepAttacksBB[pc][s]; } } -/// lsb()/msb() finds the least/most significant bit in a non-zero bitboard. -/// pop_lsb() finds and clears the least significant bit in a non-zero bitboard. + +/// lsb() and msb() return the least/most significant bit in a non-zero bitboard #ifdef USE_BSFQ @@ -304,7 +298,7 @@ FORCE_INLINE Square lsb(Bitboard b) { return (Square) (uint32_t(b) ? lsb32(uint32_t(b)) : 32 + lsb32(uint32_t(b >> 32))); } -# else +# else // Assumed gcc or compatible compiler FORCE_INLINE Square lsb(Bitboard b) { // Assembly code by Heinz van Saanen Bitboard idx; @@ -320,21 +314,24 @@ FORCE_INLINE Square msb(Bitboard b) { # endif +#else // ifdef(USE_BSFQ) + +Square lsb(Bitboard b); +Square msb(Bitboard b); + +#endif + + +/// pop_lsb() finds and clears the least significant bit in a non-zero bitboard + FORCE_INLINE Square pop_lsb(Bitboard* b) { const Square s = lsb(*b); *b &= *b - 1; return s; } -#else // if defined(USE_BSFQ) -extern Square msb(Bitboard b); -extern Square lsb(Bitboard b); -extern Square pop_lsb(Bitboard* b); - -#endif - -/// frontmost_sq() and backmost_sq() find the square corresponding to the +/// frontmost_sq() and backmost_sq() return the square corresponding to the /// most/least advanced bit relative to the given color. inline Square frontmost_sq(Color c, Bitboard b) { return c == WHITE ? msb(b) : lsb(b); } diff --git a/DroidFish/jni/stockfish/bitcount.h b/DroidFish/jni/stockfish/bitcount.h index 9feed19..8bc0617 100644 --- a/DroidFish/jni/stockfish/bitcount.h +++ b/DroidFish/jni/stockfish/bitcount.h @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -22,6 +22,7 @@ #define BITCOUNT_H_INCLUDED #include + #include "types.h" enum BitCountType { @@ -35,7 +36,7 @@ enum BitCountType { /// Determine at compile time the best popcount<> specialization according to /// whether the platform is 32 or 64 bit, the maximum number of non-zero /// bits to count and if the hardware popcnt instruction is available. -const BitCountType Full = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64 : CNT_32; +const BitCountType Full = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64 : CNT_32; const BitCountType Max15 = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64_MAX15 : CNT_32_MAX15; @@ -94,7 +95,7 @@ inline int popcount(Bitboard b) { return (int)__popcnt64(b); -#else +#else // Assumed gcc or compatible compiler return __builtin_popcountll(b); diff --git a/DroidFish/jni/stockfish/endgame.cpp b/DroidFish/jni/stockfish/endgame.cpp index 311443e..2c87b2a 100644 --- a/DroidFish/jni/stockfish/endgame.cpp +++ b/DroidFish/jni/stockfish/endgame.cpp @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -60,8 +60,8 @@ namespace { const int PushAway [8] = { 0, 5, 20, 40, 60, 80, 90, 100 }; #ifndef NDEBUG - bool verify_material(const Position& pos, Color c, Value npm, int num_pawns) { - return pos.non_pawn_material(c) == npm && pos.count(c) == num_pawns; + bool verify_material(const Position& pos, Color c, Value npm, int pawnsCnt) { + return pos.non_pawn_material(c) == npm && pos.count(c) == pawnsCnt; } #endif @@ -162,12 +162,13 @@ Value Endgame::operator()(const Position& pos) const { Value result = pos.non_pawn_material(strongSide) + pos.count(strongSide) * PawnValueEg + PushToEdges[loserKSq] - + PushClose[square_distance(winnerKSq, loserKSq)]; + + PushClose[distance(winnerKSq, loserKSq)]; if ( pos.count(strongSide) || pos.count(strongSide) ||(pos.count(strongSide) && pos.count(strongSide)) - || pos.bishop_pair(strongSide)) + ||(pos.count(strongSide) > 1 && opposite_colors(pos.list(strongSide)[0], + pos.list(strongSide)[1]))) result += VALUE_KNOWN_WIN; return strongSide == pos.side_to_move() ? result : -result; @@ -196,7 +197,7 @@ Value Endgame::operator()(const Position& pos) const { } Value result = VALUE_KNOWN_WIN - + PushClose[square_distance(winnerKSq, loserKSq)] + + PushClose[distance(winnerKSq, loserKSq)] + PushToCorners[loserKSq]; return strongSide == pos.side_to_move() ? result : -result; @@ -217,7 +218,7 @@ Value Endgame::operator()(const Position& pos) const { Color us = strongSide == pos.side_to_move() ? WHITE : BLACK; - if (!Bitbases::probe_kpk(wksq, psq, bksq, us)) + if (!Bitbases::probe(wksq, psq, bksq, us)) return VALUE_DRAW; Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(psq)); @@ -246,26 +247,26 @@ Value Endgame::operator()(const Position& pos) const { // If the stronger side's king is in front of the pawn, it's a win if (wksq < psq && file_of(wksq) == file_of(psq)) - result = RookValueEg - square_distance(wksq, psq); + result = RookValueEg - distance(wksq, psq); // If the weaker side's king is too far from the pawn and the rook, // it's a win. - else if ( square_distance(bksq, psq) >= 3 + (pos.side_to_move() == weakSide) - && square_distance(bksq, rsq) >= 3) - result = RookValueEg - square_distance(wksq, psq); + else if ( distance(bksq, psq) >= 3 + (pos.side_to_move() == weakSide) + && distance(bksq, rsq) >= 3) + result = RookValueEg - distance(wksq, psq); // If the pawn is far advanced and supported by the defending king, // the position is drawish else if ( rank_of(bksq) <= RANK_3 - && square_distance(bksq, psq) == 1 + && distance(bksq, psq) == 1 && rank_of(wksq) >= RANK_4 - && square_distance(wksq, psq) > 2 + (pos.side_to_move() == strongSide)) - result = Value(80) - 8 * square_distance(wksq, psq); + && distance(wksq, psq) > 2 + (pos.side_to_move() == strongSide)) + result = Value(80) - 8 * distance(wksq, psq); else - result = Value(200) - 8 * ( square_distance(wksq, psq + DELTA_S) - - square_distance(bksq, psq + DELTA_S) - - square_distance(psq, queeningSq)); + result = Value(200) - 8 * ( distance(wksq, psq + DELTA_S) + - distance(bksq, psq + DELTA_S) + - distance(psq, queeningSq)); return strongSide == pos.side_to_move() ? result : -result; } @@ -294,7 +295,7 @@ Value Endgame::operator()(const Position& pos) const { Square bksq = pos.king_square(weakSide); Square bnsq = pos.list(weakSide)[0]; - Value result = Value(PushToEdges[bksq] + PushAway[square_distance(bksq, bnsq)]); + Value result = Value(PushToEdges[bksq] + PushAway[distance(bksq, bnsq)]); return strongSide == pos.side_to_move() ? result : -result; } @@ -313,10 +314,10 @@ Value Endgame::operator()(const Position& pos) const { Square loserKSq = pos.king_square(weakSide); Square pawnSq = pos.list(weakSide)[0]; - Value result = Value(PushClose[square_distance(winnerKSq, loserKSq)]); + Value result = Value(PushClose[distance(winnerKSq, loserKSq)]); if ( relative_rank(weakSide, pawnSq) != RANK_7 - || square_distance(loserKSq, pawnSq) != 1 + || distance(loserKSq, pawnSq) != 1 || !((FileABB | FileCBB | FileFBB | FileHBB) & pawnSq)) result += QueenValueEg - PawnValueEg; @@ -340,7 +341,7 @@ Value Endgame::operator()(const Position& pos) const { Value result = QueenValueEg - RookValueEg + PushToEdges[loserKSq] - + PushClose[square_distance(winnerKSq, loserKSq)]; + + PushClose[distance(winnerKSq, loserKSq)]; return strongSide == pos.side_to_move() ? result : -result; } @@ -375,7 +376,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { Square kingSq = pos.king_square(weakSide); if ( opposite_colors(queeningSq, bishopSq) - && square_distance(queeningSq, kingSq) <= 1) + && distance(queeningSq, kingSq) <= 1) return SCALE_FACTOR_DRAW; } @@ -398,8 +399,8 @@ ScaleFactor Endgame::operator()(const Position& pos) const { && (pos.pieces(strongSide, PAWN) & (weakPawnSq + pawn_push(weakSide))) && (opposite_colors(bishopSq, weakPawnSq) || pos.count(strongSide) == 1)) { - int strongKingDist = square_distance(weakPawnSq, strongKingSq); - int weakKingDist = square_distance(weakPawnSq, weakKingSq); + int strongKingDist = distance(weakPawnSq, strongKingSq); + int weakKingDist = distance(weakPawnSq, weakKingSq); // It's a draw if the weak king is on its back two ranks, within 2 // squares of the blocking pawn and the strong king is not @@ -469,7 +470,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // If the pawn is not too far advanced and the defending king defends the // queening square, use the third-rank defence. if ( r <= RANK_5 - && square_distance(bksq, queeningSq) <= 1 + && distance(bksq, queeningSq) <= 1 && wksq <= SQ_H5 && (rank_of(brsq) == RANK_6 || (r <= RANK_3 && rank_of(wrsq) != RANK_6))) return SCALE_FACTOR_DRAW; @@ -477,15 +478,15 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // The defending side saves a draw by checking from behind in case the pawn // has advanced to the 6th rank with the king behind. if ( r == RANK_6 - && square_distance(bksq, queeningSq) <= 1 + && distance(bksq, queeningSq) <= 1 && rank_of(wksq) + tempo <= RANK_6 - && (rank_of(brsq) == RANK_1 || (!tempo && abs(file_of(brsq) - f) >= 3))) + && (rank_of(brsq) == RANK_1 || (!tempo && distance(file_of(brsq), f) >= 3))) return SCALE_FACTOR_DRAW; if ( r >= RANK_6 && bksq == queeningSq && rank_of(brsq) == RANK_1 - && (!tempo || square_distance(wksq, wpsq) >= 2)) + && (!tempo || distance(wksq, wpsq) >= 2)) return SCALE_FACTOR_DRAW; // White pawn on a7 and rook on a8 is a draw if black's king is on g7 or h7 @@ -501,8 +502,8 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // away, it's a draw. if ( r <= RANK_5 && bksq == wpsq + DELTA_N - && square_distance(wksq, wpsq) - tempo >= 2 - && square_distance(wksq, brsq) - tempo >= 2) + && distance(wksq, wpsq) - tempo >= 2 + && distance(wksq, brsq) - tempo >= 2) return SCALE_FACTOR_DRAW; // Pawn on the 7th rank supported by the rook from behind usually wins if the @@ -512,22 +513,22 @@ ScaleFactor Endgame::operator()(const Position& pos) const { && f != FILE_A && file_of(wrsq) == f && wrsq != queeningSq - && (square_distance(wksq, queeningSq) < square_distance(bksq, queeningSq) - 2 + tempo) - && (square_distance(wksq, queeningSq) < square_distance(bksq, wrsq) + tempo)) - return ScaleFactor(SCALE_FACTOR_MAX - 2 * square_distance(wksq, queeningSq)); + && (distance(wksq, queeningSq) < distance(bksq, queeningSq) - 2 + tempo) + && (distance(wksq, queeningSq) < distance(bksq, wrsq) + tempo)) + return ScaleFactor(SCALE_FACTOR_MAX - 2 * distance(wksq, queeningSq)); // Similar to the above, but with the pawn further back if ( f != FILE_A && file_of(wrsq) == f && wrsq < wpsq - && (square_distance(wksq, queeningSq) < square_distance(bksq, queeningSq) - 2 + tempo) - && (square_distance(wksq, wpsq + DELTA_N) < square_distance(bksq, wpsq + DELTA_N) - 2 + tempo) - && ( square_distance(bksq, wrsq) + tempo >= 3 - || ( square_distance(wksq, queeningSq) < square_distance(bksq, wrsq) + tempo - && (square_distance(wksq, wpsq + DELTA_N) < square_distance(bksq, wrsq) + tempo)))) + && (distance(wksq, queeningSq) < distance(bksq, queeningSq) - 2 + tempo) + && (distance(wksq, wpsq + DELTA_N) < distance(bksq, wpsq + DELTA_N) - 2 + tempo) + && ( distance(bksq, wrsq) + tempo >= 3 + || ( distance(wksq, queeningSq) < distance(bksq, wrsq) + tempo + && (distance(wksq, wpsq + DELTA_N) < distance(bksq, wrsq) + tempo)))) return ScaleFactor( SCALE_FACTOR_MAX - - 8 * square_distance(wpsq, queeningSq) - - 2 * square_distance(wksq, queeningSq)); + - 8 * distance(wpsq, queeningSq) + - 2 * distance(wksq, queeningSq)); // If the pawn is not far advanced and the defending king is somewhere in // the pawn's path, it's probably a draw. @@ -535,9 +536,9 @@ ScaleFactor Endgame::operator()(const Position& pos) const { { if (file_of(bksq) == file_of(wpsq)) return ScaleFactor(10); - if ( abs(file_of(bksq) - file_of(wpsq)) == 1 - && square_distance(wksq, bksq) > 2) - return ScaleFactor(24 - 2 * square_distance(wksq, bksq)); + if ( distance(bksq, wpsq) == 1 + && distance(wksq, bksq) > 2) + return ScaleFactor(24 - 2 * distance(wksq, bksq)); } return SCALE_FACTOR_NONE; } @@ -564,7 +565,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // corner but not trapped there. if (rk == RANK_5 && !opposite_colors(bsq, psq)) { - int d = square_distance(psq + 3 * push, ksq); + int d = distance(psq + 3 * push, ksq); if (d <= 2 && !(d == 0 && ksq == pos.king_square(strongSide) + 2 * push)) return ScaleFactor(24); @@ -577,9 +578,9 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // pawn from a reasonable distance and the defending king is near // the corner if ( rk == RANK_6 - && square_distance(psq + 2 * push, ksq) <= 1 + && distance(psq + 2 * push, ksq) <= 1 && (PseudoAttacks[BISHOP][bsq] & (psq + push)) - && file_distance(bsq, psq) >= 2) + && distance(bsq, psq) >= 2) return ScaleFactor(8); } @@ -604,8 +605,8 @@ ScaleFactor Endgame::operator()(const Position& pos) const { Rank r = std::max(relative_rank(strongSide, wpsq1), relative_rank(strongSide, wpsq2)); - if ( file_distance(bksq, wpsq1) <= 1 - && file_distance(bksq, wpsq2) <= 1 + if ( distance(bksq, wpsq1) <= 1 + && distance(bksq, wpsq2) <= 1 && relative_rank(strongSide, bksq) > r) { switch (r) { @@ -638,7 +639,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // the king is within one file of the pawns, it's a draw. if ( !(pawns & ~in_front_bb(weakSide, rank_of(ksq))) && !((pawns & ~FileABB) && (pawns & ~FileHBB)) - && file_distance(ksq, psq) <= 1) + && distance(ksq, psq) <= 1) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; @@ -690,7 +691,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { return SCALE_FACTOR_DRAW; if ( (pos.attacks_from(weakBishopSq) & path) - && square_distance(weakBishopSq, pawnSq) >= 3) + && distance(weakBishopSq, pawnSq) >= 3) return SCALE_FACTOR_DRAW; } } @@ -729,7 +730,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { blockSq2 = make_square(file_of(psq1), rank_of(psq2)); } - switch (file_distance(psq1, psq2)) + switch (distance(psq1, psq2)) { case 0: // Both pawns are on the same file. It's an easy draw if the defender firmly @@ -749,7 +750,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { && opposite_colors(ksq, wbsq) && ( bbsq == blockSq2 || (pos.attacks_from(blockSq2) & pos.pieces(weakSide, BISHOP)) - || abs(r1 - r2) >= 2)) + || distance(r1, r2) >= 2)) return SCALE_FACTOR_DRAW; else if ( ksq == blockSq2 @@ -802,7 +803,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { Square pawnSq = normalize(pos, strongSide, pos.list(strongSide)[0]); Square weakKingSq = normalize(pos, strongSide, pos.king_square(weakSide)); - if (pawnSq == SQ_A7 && square_distance(SQ_A8, weakKingSq) <= 1) + if (pawnSq == SQ_A7 && distance(SQ_A8, weakKingSq) <= 1) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; @@ -821,7 +822,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // King needs to get close to promoting pawn to prevent knight from blocking. // Rules for this are very tricky, so just approximate. if (forward_bb(strongSide, pawnSq) & pos.attacks_from(bishopSq)) - return ScaleFactor(square_distance(weakKingSq, pawnSq)); + return ScaleFactor(distance(weakKingSq, pawnSq)); return SCALE_FACTOR_NONE; } @@ -852,5 +853,5 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw, // it's probably at least a draw even with the pawn. - return Bitbases::probe_kpk(wksq, psq, bksq, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW; + return Bitbases::probe(wksq, psq, bksq, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW; } diff --git a/DroidFish/jni/stockfish/endgame.h b/DroidFish/jni/stockfish/endgame.h index c3d5dd0..d7a7681 100644 --- a/DroidFish/jni/stockfish/endgame.h +++ b/DroidFish/jni/stockfish/endgame.h @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -45,7 +45,7 @@ enum EndgameType { // Scaling functions - SCALE_FUNS, + SCALING_FUNCTIONS, KBPsK, // KB and pawns vs K KQKRPs, // KQ vs KR and pawns @@ -76,26 +76,26 @@ template struct EndgameBase { virtual ~EndgameBase() {} - virtual Color color() const = 0; + virtual Color strong_side() const = 0; virtual T operator()(const Position&) const = 0; }; -template SCALE_FUNS)>::type> +template SCALING_FUNCTIONS)>::type> struct Endgame : public EndgameBase { explicit Endgame(Color c) : strongSide(c), weakSide(~c) {} - Color color() const { return strongSide; } + Color strong_side() const { return strongSide; } T operator()(const Position&) const; private: - const Color strongSide, weakSide; + Color strongSide, weakSide; }; /// The Endgames class stores the pointers to endgame evaluation and scaling -/// base objects in two std::map typedefs. We then use polymorphism to invoke -/// the actual endgame function by calling its virtual operator(). +/// base objects in two std::map. We use polymorphism to invoke the actual +/// endgame function by calling its virtual operator(). class Endgames { @@ -114,8 +114,9 @@ public: Endgames(); ~Endgames(); - template T probe(Key key, T& eg) - { return eg = map(eg).count(key) ? map(eg)[key] : NULL; } + template T probe(Key key, T& eg) { + return eg = map(eg).count(key) ? map(eg)[key] : NULL; + } }; #endif // #ifndef ENDGAME_H_INCLUDED diff --git a/DroidFish/jni/stockfish/evaluate.cpp b/DroidFish/jni/stockfish/evaluate.cpp index 69df762..45c5b0a 100644 --- a/DroidFish/jni/stockfish/evaluate.cpp +++ b/DroidFish/jni/stockfish/evaluate.cpp @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -19,6 +19,7 @@ #include #include +#include // For std::memset #include #include @@ -26,8 +27,6 @@ #include "evaluate.h" #include "material.h" #include "pawns.h" -#include "thread.h" -#include "ucioption.h" namespace { @@ -58,9 +57,8 @@ namespace { // kingAttackersWeight[color] is the sum of the "weight" of the pieces of the // given color which attack a square in the kingRing of the enemy king. The - // weights of the individual piece types are given by the variables - // QueenAttackWeight, RookAttackWeight, BishopAttackWeight and - // KnightAttackWeight in evaluate.cpp + // weights of the individual piece types are given by the elements in the + // KingAttackWeights array. int kingAttackersWeight[COLOR_NB]; // kingAdjacentZoneAttacksCount[color] is the number of attacks to squares @@ -79,23 +77,24 @@ namespace { MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, TOTAL, TERMS_NB }; - Score terms[COLOR_NB][TERMS_NB]; + Score scores[COLOR_NB][TERMS_NB]; EvalInfo ei; ScaleFactor sf; double to_cp(Value v); - void add_term(int idx, Score term_w, Score term_b = SCORE_ZERO); - void format_row(std::stringstream& ss, const char* name, int idx); + void write(int idx, Color c, Score s); + void write(int idx, Score w, Score b = SCORE_ZERO); + void print(std::stringstream& ss, const char* name, int idx); std::string do_trace(const Position& pos); } // 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} + {289, 344}, {233, 201}, {221, 273}, {46, 0}, {321, 0} }; - typedef Value V; + #define V(v) Value(v) #define S(mg, eg) make_score(mg, eg) // MobilityBonus[PieceType][attacked] contains bonuses for middle and end @@ -137,30 +136,32 @@ namespace { V(0), V(5), V(8), V(8), V(8), V(8), V(5), V(0) } }; - // Threat[attacking][attacked] contains bonuses according to which piece - // type attacks which one. - const Score Threat[][PIECE_TYPE_NB] = { - { S(0, 0), S( 7, 39), S(24, 49), S(24, 49), S(41,100), S(41,100) }, // Minor - { S(0, 0), S(15, 39), S(15, 45), S(15, 45), S(15, 45), S(24, 49) } // Major + // Threat[defended/weak][minor/major attacking][attacked PieceType] contains + // bonuses according to which piece type attacks which one. + const Score Threat[][2][PIECE_TYPE_NB] = { + { { S(0, 0), S( 0, 0), S(19, 37), S(24, 37), S(44, 97), S(35,106) }, // Defended Minor + { S(0, 0), S( 0, 0), S( 9, 14), S( 9, 14), S( 7, 14), S(24, 48) } }, // Defended Major + { { S(0, 0), S( 0,32), S(33, 41), S(31, 50), S(41,100), S(35,104) }, // Weak Minor + { S(0, 0), S( 0,27), S(26, 57), S(26, 57), S(0 , 43), S(23, 51) } } // Weak Major }; // 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(80, 119), S(80, 119), S(117, 199), S(127, 218) + S(0, 0), S(0, 0), S(87, 118), S(84, 122), S(114, 203), S(121, 217) }; // 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); + const Score KingOnOne = S( 2, 58); + const Score KingOnMany = S( 6,125); + const Score RookOnPawn = S( 7, 27); + const Score RookOnOpenFile = S(43, 21); + const Score RookOnSemiOpenFile = 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(31, 26); // 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 @@ -168,6 +169,7 @@ namespace { const Score TrappedBishopA1H1 = S(50, 50); #undef S + #undef V // SpaceMask[Color] contains the area of the board which is considered // by the space evaluation. In the middlegame, each side is given a bonus @@ -178,31 +180,29 @@ namespace { (FileCBB | FileDBB | FileEBB | FileFBB) & (Rank7BB | Rank6BB | Rank5BB) }; - // King danger constants and variables. The king danger scores are taken - // from KingDanger[]. Various little "meta-bonuses" measuring the strength + // King danger constants and variables. The king danger scores are looked-up + // in KingDanger[]. Various little "meta-bonuses" measuring the strength // of the enemy attack are added up into an integer, which is used as an // index to KingDanger[]. // // KingAttackWeights[PieceType] contains king attack weights by piece type - const int KingAttackWeights[] = { 0, 0, 2, 2, 3, 5 }; + const int KingAttackWeights[] = { 0, 0, 6, 2, 5, 5 }; // Bonuses for enemy's safe checks - const int QueenContactCheck = 24; - const int RookContactCheck = 16; - const int QueenCheck = 12; - const int RookCheck = 8; - const int BishopCheck = 2; - const int KnightCheck = 3; + const int QueenContactCheck = 92; + const int RookContactCheck = 68; + const int QueenCheck = 50; + const int RookCheck = 36; + const int BishopCheck = 7; + const int KnightCheck = 14; // KingDanger[attackUnits] contains the actual king danger weighted // scores, indexed by a calculated integer number. - Score KingDanger[128]; + Score KingDanger[512]; - 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) { - return make_score(mg_value(v) * w.mg / 256, eg_value(v) * w.eg / 256); + // apply_weight() weighs score 's' by weight 'w' trying to prevent overflow + Score apply_weight(Score s, const Weight& w) { + return make_score(mg_value(s) * w.mg / 256, eg_value(s) * w.eg / 256); } @@ -221,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.non_pawn_material(Us) > QueenValueMg + PawnValueMg) + if (pos.non_pawn_material(Us) >= QueenValueMg) { ei.kingRing[Them] = b | shift_bb(b); b &= ei.attackedBy[Us][PAWN]; @@ -313,65 +313,65 @@ namespace { if (Pt == BISHOP || Pt == KNIGHT) { - // Penalty for bishop with same colored pawns - if (Pt == BISHOP) - score -= BishopPawns * ei.pi->pawns_on_same_color_squares(Us, s); - - // Bishop and knight outpost square + // Bonus for outpost square if (!(pos.pieces(Them, PAWN) & pawn_attack_span(Us, s))) score += evaluate_outpost(pos, ei, s); - // Bishop or knight behind a pawn + // Bonus when behind a pawn if ( relative_rank(Us, s) < RANK_5 && (pos.pieces(PAWN) & (s + pawn_push(Us)))) score += MinorBehindPawn; + + // Penalty for pawns on same color square of bishop + if (Pt == BISHOP) + score -= BishopPawns * ei.pi->pawns_on_same_color_squares(Us, s); + + // An important Chess960 pattern: A cornered bishop blocked by a friendly + // pawn diagonally in front of it is a very serious problem, especially + // when that pawn is also blocked. + if ( Pt == BISHOP + && pos.is_chess960() + && (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1))) + { + Square d = pawn_push(Us) + (file_of(s) == FILE_A ? DELTA_E : DELTA_W); + if (pos.piece_on(s + d) == make_piece(Us, PAWN)) + score -= !pos.empty(s + d + pawn_push(Us)) ? TrappedBishopA1H1 * 4 + : pos.piece_on(s + d + d) == make_piece(Us, PAWN) ? TrappedBishopA1H1 * 2 + : TrappedBishopA1H1; + } } if (Pt == ROOK) { - // Rook piece attacking enemy pawns on the same rank/file + // Bonus for aligning with enemy pawns on the same rank/file if (relative_rank(Us, s) >= RANK_5) { - Bitboard pawns = pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]; - if (pawns) - score += popcount(pawns) * RookOnPawn; + Bitboard alignedPawns = pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]; + if (alignedPawns) + score += popcount(alignedPawns) * RookOnPawn; } - // Give a bonus for a rook on a open or semi-open file + // Bonus when on an 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)) ? RookOnOpenFile : RookOnSemiOpenFile; - if (mob > 3 || ei.pi->semiopen_file(Us, file_of(s))) - continue; + // Penalize when trapped by the king, even more if king cannot castle + if (mob <= 3 && !ei.pi->semiopen_file(Us, file_of(s))) + { + Square ksq = pos.king_square(Us); - Square ksq = pos.king_square(Us); - - // Penalize rooks which are trapped by a king. Penalize more if the - // king has lost its castling capability. - 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 * 22, 0)) * (1 + !pos.can_castle(Us)); - } - - // An important Chess960 pattern: A cornered bishop blocked by a friendly - // pawn diagonally in front of it is a very serious problem, especially - // when that pawn is also blocked. - if ( Pt == BISHOP - && pos.is_chess960() - && (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1))) - { - Square d = pawn_push(Us) + (file_of(s) == FILE_A ? DELTA_E : DELTA_W); - if (pos.piece_on(s + d) == make_piece(Us, PAWN)) - score -= !pos.empty(s + d + pawn_push(Us)) ? TrappedBishopA1H1 * 4 - : pos.piece_on(s + d + d) == make_piece(Us, PAWN) ? TrappedBishopA1H1 * 2 - : TrappedBishopA1H1; + 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 * 22, 0)) * (1 + !pos.can_castle(Us)); + } } } if (Trace) - Tracing::terms[Us][Pt] = score; + Tracing::write(Pt, Us, score); + // Recursively call evaluate_pieces() of next piece type until KING excluded return score - evaluate_pieces(pos, ei, mobility, mobilityArea); } @@ -407,33 +407,32 @@ namespace { | ei.attackedBy[Us][QUEEN]); // Initialize the 'attackUnits' variable, which is used later on as an - // index to the KingDanger[] array. The initial value is based on the + // index into the KingDanger[] array. The initial value is based on the // number and types of the enemy's attacking pieces, the number of // attacked and undefended squares around our king and the quality of // the pawn shelter (current 'score' value). - 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 - - !pos.count(Them) * 15; + attackUnits = std::min(77, ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) + + 10 * ei.kingAdjacentZoneAttacksCount[Them] + + 19 * popcount(undefended) + + 9 * (ei.pinnedPieces[Us] != 0) + - mg_value(score) * 63 / 512 + - !pos.count(Them) * 60; // Analyse the enemy's safe queen contact checks. Firstly, find the - // undefended squares around the king that are attacked by the enemy's - // queen... + // undefended squares around the king reachable by the enemy queen... b = undefended & ei.attackedBy[Them][QUEEN] & ~pos.pieces(Them); if (b) { // ...and then remove squares not supported by another enemy piece - b &= ( ei.attackedBy[Them][PAWN] | ei.attackedBy[Them][KNIGHT] - | ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][ROOK]); + b &= ei.attackedBy[Them][PAWN] | ei.attackedBy[Them][KNIGHT] + | ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][ROOK]; if (b) - attackUnits += QueenContactCheck * popcount(b); + attackUnits += QueenContactCheck * popcount(b); } // Analyse the enemy's safe rook contact checks. Firstly, find the - // undefended squares around the king that are attacked by the enemy's - // rooks... + // undefended squares around the king reachable by the enemy rooks... b = undefended & ei.attackedBy[Them][ROOK] & ~pos.pieces(Them); // Consider only squares where the enemy's rook gives check @@ -446,13 +445,13 @@ namespace { | ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][QUEEN]); if (b) - attackUnits += RookContactCheck * popcount(b); + attackUnits += RookContactCheck * popcount(b); } // Analyse the enemy's safe distance checks for sliders and knights - safe = ~(pos.pieces(Them) | ei.attackedBy[Us][ALL_PIECES]); + safe = ~(ei.attackedBy[Us][ALL_PIECES] | pos.pieces(Them)); - b1 = pos.attacks_from(ksq) & safe; + b1 = pos.attacks_from(ksq) & safe; b2 = pos.attacks_from(ksq) & safe; // Enemy queen safe checks @@ -475,16 +474,13 @@ namespace { if (b) attackUnits += KnightCheck * popcount(b); - // To index KingDanger[] attackUnits must be in [0, 99] range - attackUnits = std::min(99, std::max(0, attackUnits)); - // Finally, extract the king danger score from the KingDanger[] // array and subtract the score from evaluation. - score -= KingDanger[attackUnits]; + score -= KingDanger[std::max(std::min(attackUnits, 399), 0)]; } if (Trace) - Tracing::terms[Us][KING] = score; + Tracing::write(KING, Us, score); return score; } @@ -498,45 +494,55 @@ namespace { const Color Them = (Us == WHITE ? BLACK : WHITE); - Bitboard b, weakEnemies, protectedEnemies; - Score score = SCORE_ZERO; + enum { Defended, Weak }; 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]); + Bitboard b, weak, defended; + Score score = SCORE_ZERO; - if (protectedEnemies) - score += Threat[Minor][type_of(pos.piece_on(lsb(protectedEnemies)))]; + // Non-pawn enemies defended by a pawn + defended = (pos.pieces(Them) ^ pos.pieces(Them, PAWN)) + & ei.attackedBy[Them][PAWN]; + + // Add a bonus according to the kind of attacking pieces + if (defended) + { + b = defended & (ei.attackedBy[Us][KNIGHT] | ei.attackedBy[Us][BISHOP]); + while (b) + score += Threat[Defended][Minor][type_of(pos.piece_on(pop_lsb(&b)))]; + + b = defended & (ei.attackedBy[Us][ROOK]); + while (b) + score += Threat[Defended][Major][type_of(pos.piece_on(pop_lsb(&b)))]; + } // Enemies not defended by a pawn and under our attack - weakEnemies = pos.pieces(Them) - & ~ei.attackedBy[Them][PAWN] - & ei.attackedBy[Us][ALL_PIECES]; + weak = pos.pieces(Them) + & ~ei.attackedBy[Them][PAWN] + & ei.attackedBy[Us][ALL_PIECES]; - // Add a bonus according if the attacking pieces are minor or major - if (weakEnemies) + // Add a bonus according to the kind of attacking pieces + if (weak) { - b = weakEnemies & (ei.attackedBy[Us][KNIGHT] | ei.attackedBy[Us][BISHOP]); - if (b) - score += Threat[Minor][type_of(pos.piece_on(lsb(b)))]; + b = weak & (ei.attackedBy[Us][KNIGHT] | ei.attackedBy[Us][BISHOP]); + while (b) + score += Threat[Weak][Minor][type_of(pos.piece_on(pop_lsb(&b)))]; - b = weakEnemies & (ei.attackedBy[Us][ROOK] | ei.attackedBy[Us][QUEEN]); - if (b) - score += Threat[Major][type_of(pos.piece_on(lsb(b)))]; + b = weak & (ei.attackedBy[Us][ROOK] | ei.attackedBy[Us][QUEEN]); + while (b) + score += Threat[Weak][Major][type_of(pos.piece_on(pop_lsb(&b)))]; - b = weakEnemies & ~ei.attackedBy[Them][ALL_PIECES]; + b = weak & ~ei.attackedBy[Them][ALL_PIECES]; if (b) - score += more_than_one(b) ? Hanging * popcount(b) : Hanging; + score += Hanging * popcount(b); - b = weakEnemies & ei.attackedBy[Us][KING]; + b = weak & ei.attackedBy[Us][KING]; if (b) score += more_than_one(b) ? KingOnMany : KingOnOne; } if (Trace) - Tracing::terms[Us][Tracing::THREAT] = score; + Tracing::write(Tracing::THREAT, Us, score); return score; } @@ -571,12 +577,12 @@ namespace { Square blockSq = s + pawn_push(Us); // Adjust bonus based on the king's proximity - ebonus += square_distance(pos.king_square(Them), blockSq) * 5 * rr - - square_distance(pos.king_square(Us ), blockSq) * 2 * rr; + ebonus += distance(pos.king_square(Them), blockSq) * 5 * rr + - distance(pos.king_square(Us ), blockSq) * 2 * rr; // If blockSq is not the queening square then consider also a second push if (relative_rank(Us, blockSq) != RANK_8) - ebonus -= square_distance(pos.king_square(Us), blockSq + pawn_push(Us)) * rr; + ebonus -= distance(pos.king_square(Us), blockSq + pawn_push(Us)) * rr; // If the pawn is free to advance, then increase the bonus if (pos.empty(blockSq)) @@ -619,33 +625,21 @@ namespace { } if (Trace) - Tracing::terms[Us][Tracing::PASSED] = apply_weight(score, Weights[PassedPawns]); + Tracing::write(Tracing::PASSED, Us, apply_weight(score, Weights[PassedPawns])); // Add the scores to the middlegame and endgame eval return apply_weight(score, Weights[PassedPawns]); } - // 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(Color us, const EvalInfo& ei) { - - Bitboard b = ei.pi->passed_pawns(us); - - return b ? Unstoppable * int(relative_rank(us, frontmost_sq(us, b))) : SCORE_ZERO; - } - - // evaluate_space() computes the space evaluation for a given side. The // space evaluation is a simple bonus based on the number of safe squares // available for minor pieces on the central four files on ranks 2--4. Safe // squares one, two or three squares behind a friendly pawn are counted - // twice. Finally, the space bonus is scaled by a weight taken from the - // material hash table. The aim is to improve play on game opening. + // twice. Finally, the space bonus is multiplied by a weight. The aim is to + // improve play on game opening. template - int evaluate_space(const Position& pos, const EvalInfo& ei) { + Score evaluate_space(const Position& pos, const EvalInfo& ei) { const Color Them = (Us == WHITE ? BLACK : WHITE); @@ -666,7 +660,11 @@ namespace { assert(unsigned(safe >> (Us == WHITE ? 32 : 0)) == 0); // Count safe + (behind & safe) with a single popcount - return popcount((Us == WHITE ? safe << 32 : safe >> 32) | (behind & safe)); + int bonus = popcount((Us == WHITE ? safe << 32 : safe >> 32) | (behind & safe)); + int weight = pos.count(Us) + pos.count(Us) + + pos.count(Them) + pos.count(Them); + + return make_score(bonus * weight * weight, 0); } @@ -679,7 +677,6 @@ namespace { EvalInfo ei; Score score, mobility[2] = { SCORE_ZERO, SCORE_ZERO }; - Thread* thisThread = pos.this_thread(); // Initialize score by reading the incrementally updated scores included // in the position object (material + piece square tables). @@ -687,8 +684,8 @@ namespace { score = pos.psq_score(); // Probe the material hash table - ei.mi = Material::probe(pos, thisThread->materialTable, thisThread->endgames); - score += ei.mi->material_value(); + ei.mi = Material::probe(pos); + score += ei.mi->imbalance(); // If we have a specialized evaluation function for the current material // configuration, call it and return. @@ -696,8 +693,8 @@ namespace { return ei.mi->evaluate(pos); // Probe the pawn hash table - ei.pi = Pawns::probe(pos, thisThread->pawnsTable); - score += apply_weight(ei.pi->pawns_value(), Weights[PawnStructure]); + ei.pi = Pawns::probe(pos); + score += apply_weight(ei.pi->pawns_score(), Weights[PawnStructure]); // Initialize attack and king safety bitboards init_eval_info(pos, ei); @@ -729,14 +726,20 @@ namespace { // 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()) { - int s = evaluate_space(pos, ei) - evaluate_space(pos, ei); - score += apply_weight(s * ei.mi->space_weight(), Weights[Space]); + Bitboard b; + if ((b = ei.pi->passed_pawns(WHITE)) != 0) + score += int(relative_rank(WHITE, frontmost_sq(WHITE, b))) * Unstoppable; + + if ((b = ei.pi->passed_pawns(BLACK)) != 0) + score -= int(relative_rank(BLACK, frontmost_sq(BLACK, b))) * Unstoppable; + } + + // Evaluate space for both sides, only during opening + if (pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) >= 2 * QueenValueMg + 4 * RookValueMg + 2 * KnightValueMg) + { + Score s = evaluate_space(pos, ei) - evaluate_space(pos, ei); + score += apply_weight(s, Weights[Space]); } // Scale winning side if position is more drawish than it appears @@ -748,27 +751,25 @@ namespace { if ( ei.mi->game_phase() < PHASE_MIDGAME && (sf == SCALE_FACTOR_NORMAL || sf == SCALE_FACTOR_ONEPAWN)) { - if (pos.opposite_bishops()) { - // Ignoring any pawns, do both sides only have a single bishop and no - // other pieces? + if (pos.opposite_bishops()) + { + // Endgame with opposite-colored bishops and no other pieces (ignoring pawns) + // is almost a draw, in case of KBP vs KB is even more a draw. 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); - } + sf = more_than_one(pos.pieces(PAWN)) ? ScaleFactor(32) : ScaleFactor(8); + + // Endgame with opposite-colored bishops, but also other pieces. Still + // a bit drawish, but not as drawish as with only the two bishops. 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)]); } + // Endings where weaker side can place his king in front of the opponent's + // pawns are drawish. + else if ( abs(eg_value(score)) <= BishopValueEg + && ei.pi->pawn_span(strongSide) <= 1 + && !pos.pawn_passed(~strongSide, pos.king_square(~strongSide))) + sf = ei.pi->pawn_span(strongSide) ? ScaleFactor(56) : ScaleFactor(38); } // Interpolate between a middlegame and a (scaled by 'sf') endgame score @@ -780,20 +781,19 @@ namespace { // In case of tracing add all single evaluation contributions for both white and black if (Trace) { - 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]) - , apply_weight(mobility[BLACK], Weights[Mobility])); - Score w = ei.mi->space_weight() * evaluate_space(pos, ei); - Score b = ei.mi->space_weight() * evaluate_space(pos, ei); - Tracing::add_term(Tracing::SPACE, apply_weight(w, Weights[Space]), apply_weight(b, Weights[Space])); - Tracing::add_term(Tracing::TOTAL, score); + Tracing::write(Tracing::MATERIAL, pos.psq_score()); + Tracing::write(Tracing::IMBALANCE, ei.mi->imbalance()); + Tracing::write(PAWN, ei.pi->pawns_score()); + Tracing::write(Tracing::MOBILITY, apply_weight(mobility[WHITE], Weights[Mobility]) + , apply_weight(mobility[BLACK], Weights[Mobility])); + Tracing::write(Tracing::SPACE, apply_weight(evaluate_space(pos, ei), Weights[Space]) + , apply_weight(evaluate_space(pos, ei), Weights[Space])); + Tracing::write(Tracing::TOTAL, score); Tracing::ei = ei; Tracing::sf = sf; } - return pos.side_to_move() == WHITE ? v : -v; + return (pos.side_to_move() == WHITE ? v : -v) + Eval::Tempo; } @@ -801,16 +801,18 @@ namespace { double Tracing::to_cp(Value v) { return double(v) / PawnValueEg; } - void Tracing::add_term(int idx, Score wScore, Score bScore) { + void Tracing::write(int idx, Color c, Score s) { scores[c][idx] = s; } - terms[WHITE][idx] = wScore; - terms[BLACK][idx] = bScore; + void Tracing::write(int idx, Score w, Score b) { + + write(idx, WHITE, w); + write(idx, BLACK, b); } - void Tracing::format_row(std::stringstream& ss, const char* name, int idx) { + void Tracing::print(std::stringstream& ss, const char* name, int idx) { - Score wScore = terms[WHITE][idx]; - Score bScore = terms[BLACK][idx]; + Score wScore = scores[WHITE][idx]; + Score bScore = scores[BLACK][idx]; switch (idx) { case MATERIAL: case IMBALANCE: case PAWN: case TOTAL: @@ -831,7 +833,7 @@ namespace { std::string Tracing::do_trace(const Position& pos) { - std::memset(terms, 0, sizeof(terms)); + std::memset(scores, 0, sizeof(scores)); Value v = do_evaluate(pos); v = pos.side_to_move() == WHITE ? v : -v; // White's point of view @@ -842,21 +844,21 @@ namespace { << " | MG EG | MG EG | MG EG \n" << "----------------+-------------+-------------+-------------\n"; - 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); - format_row(ss, "Rooks", ROOK); - format_row(ss, "Queens", QUEEN); - format_row(ss, "Mobility", MOBILITY); - format_row(ss, "King safety", KING); - format_row(ss, "Threats", THREAT); - format_row(ss, "Passed pawns", PASSED); - format_row(ss, "Space", SPACE); + print(ss, "Material", MATERIAL); + print(ss, "Imbalance", IMBALANCE); + print(ss, "Pawns", PAWN); + print(ss, "Knights", KNIGHT); + print(ss, "Bishops", BISHOP); + print(ss, "Rooks", ROOK); + print(ss, "Queens", QUEEN); + print(ss, "Mobility", MOBILITY); + print(ss, "King safety", KING); + print(ss, "Threats", THREAT); + print(ss, "Passed pawns", PASSED); + print(ss, "Space", SPACE); ss << "----------------+-------------+-------------+-------------\n"; - format_row(ss, "Total", TOTAL); + print(ss, "Total", TOTAL); ss << "\nTotal Evaluation: " << to_cp(v) << " (white side)\n"; @@ -872,7 +874,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) + Tempo; + return do_evaluate(pos); } @@ -885,18 +887,18 @@ namespace Eval { } - /// init() computes evaluation weights from the corresponding UCI parameters - /// and setup king tables. + /// init() computes evaluation weights, usually at startup void init() { - const double MaxSlope = 30; + const double MaxSlope = 7.5; const double Peak = 1280; + double t = 0.0; - for (int t = 0, i = 1; i < 100; ++i) + for (int i = 1; i < 400; ++i) { - t = int(std::min(Peak, std::min(0.4 * i * i, t + MaxSlope))); - KingDanger[i] = apply_weight(make_score(t, 0), Weights[KingSafety]); + t = std::min(Peak, std::min(0.025 * i * i, t + MaxSlope)); + KingDanger[i] = apply_weight(make_score(int(t), 0), Weights[KingSafety]); } } diff --git a/DroidFish/jni/stockfish/evaluate.h b/DroidFish/jni/stockfish/evaluate.h index fff2476..d859582 100644 --- a/DroidFish/jni/stockfish/evaluate.h +++ b/DroidFish/jni/stockfish/evaluate.h @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -20,6 +20,8 @@ #ifndef EVALUATE_H_INCLUDED #define EVALUATE_H_INCLUDED +#include + #include "types.h" class Position; @@ -28,9 +30,9 @@ 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); +void init(); +Value evaluate(const Position& pos); +std::string trace(const Position& pos); } diff --git a/DroidFish/jni/stockfish/main.cpp b/DroidFish/jni/stockfish/main.cpp index 69d29c2..8cdf9e6 100644 --- a/DroidFish/jni/stockfish/main.cpp +++ b/DroidFish/jni/stockfish/main.cpp @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -23,10 +23,10 @@ #include "evaluate.h" #include "position.h" #include "search.h" -#include "tbprobe.h" #include "thread.h" #include "tt.h" -#include "ucioption.h" +#include "uci.h" +#include "syzygy/tbprobe.h" int main(int argc, char* argv[]) { @@ -35,12 +35,13 @@ int main(int argc, char* argv[]) { UCI::init(Options); Bitboards::init(); Position::init(); - Bitbases::init_kpk(); + Bitbases::init(); Search::init(); Eval::init(); + Pawns::init(); Threads.init(); - TT.resize(Options["Hash"]); Tablebases::init(Options["SyzygyPath"]); + TT.resize(Options["Hash"]); UCI::loop(argc, argv); diff --git a/DroidFish/jni/stockfish/material.cpp b/DroidFish/jni/stockfish/material.cpp index b7db134..094a13d 100644 --- a/DroidFish/jni/stockfish/material.cpp +++ b/DroidFish/jni/stockfish/material.cpp @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -17,33 +17,34 @@ along with this program. If not, see . */ -#include // For std::min +#include // For std::min #include -#include +#include // For std::memset #include "material.h" +#include "thread.h" using namespace std; namespace { - // Polynomial material balance parameters + // Polynomial material imbalance parameters // pair pawn knight bishop rook queen const int Linear[6] = { 1852, -162, -1122, -183, 249, -154 }; - const int QuadraticSameSide[][PIECE_TYPE_NB] = { + const int QuadraticOurs[][PIECE_TYPE_NB] = { // OUR PIECES // pair pawn knight bishop rook queen { 0 }, // Bishop pair { 39, 2 }, // Pawn - { 35, 271, -4 }, // knight OUR PIECES + { 35, 271, -4 }, // Knight OUR PIECES { 0, 105, 4, 0 }, // Bishop { -27, -2, 46, 100, -141 }, // Rook {-177, 25, 129, 142, -137, 0 } // Queen }; - const int QuadraticOppositeSide[][PIECE_TYPE_NB] = { + const int QuadraticTheirs[][PIECE_TYPE_NB] = { // THEIR PIECES // pair pawn knight bishop rook queen { 0 }, // Bishop pair @@ -56,7 +57,7 @@ namespace { // Endgame evaluation and scaling functions are accessed directly and not through // the function maps because they correspond to more than one material hash key. - Endgame EvaluateKXK[] = { Endgame(WHITE), Endgame(BLACK) }; + Endgame EvaluateKXK[] = { Endgame(WHITE), Endgame(BLACK) }; Endgame ScaleKBPsK[] = { Endgame(WHITE), Endgame(BLACK) }; Endgame ScaleKQKRPs[] = { Endgame(WHITE), Endgame(BLACK) }; @@ -104,8 +105,8 @@ namespace { int v = Linear[pt1]; for (int pt2 = NO_PIECE_TYPE; pt2 <= pt1; ++pt2) - v += QuadraticSameSide[pt1][pt2] * pieceCount[Us][pt2] - + QuadraticOppositeSide[pt1][pt2] * pieceCount[Them][pt2]; + v += QuadraticOurs[pt1][pt2] * pieceCount[Us][pt2] + + QuadraticTheirs[pt1][pt2] * pieceCount[Them][pt2]; bonus += pieceCount[Us][pt1] * v; } @@ -117,19 +118,16 @@ namespace { namespace Material { -/// Material::probe() takes a position object as input, looks up a MaterialEntry -/// object, and returns a pointer to it. If the material configuration is not -/// already present in the table, it is computed and stored there, so we don't -/// have to recompute everything when the same material configuration occurs again. +/// Material::probe() looks up the current position's material configuration in +/// the material hash table. It returns a pointer to the Entry if the position +/// is found. Otherwise a new Entry is computed and stored there, so we don't +/// have to recompute all when the same material configuration occurs again. -Entry* probe(const Position& pos, Table& entries, Endgames& endgames) { +Entry* probe(const Position& pos) { Key key = pos.material_key(); - Entry* e = entries[key]; + Entry* e = pos.this_thread()->materialTable[key]; - // If e->key matches the position's material hash key, it means that we - // have analysed this material configuration before, and we can simply - // return the information we found the last time instead of recomputing it. if (e->key == key) return e; @@ -141,7 +139,7 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) { // Let's look if we have a specialized evaluation function for this particular // material configuration. Firstly we look for a fixed configuration one, then // for a generic one if the previous search failed. - if (endgames.probe(key, e->evaluationFunction)) + if (pos.this_thread()->endgames.probe(key, e->evaluationFunction)) return e; if (is_KXK(pos)) @@ -156,22 +154,19 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) { return e; } - // OK, we didn't find any special evaluation function for the current - // material configuration. Is there a suitable scaling function? - // - // We face problems when there are several conflicting applicable - // scaling functions and we need to decide which one to use. + // OK, we didn't find any special evaluation function for the current material + // configuration. Is there a suitable specialized scaling function? EndgameBase* sf; - if (endgames.probe(key, sf)) + if (pos.this_thread()->endgames.probe(key, sf)) { - e->scalingFunction[sf->color()] = sf; + e->scalingFunction[sf->strong_side()] = sf; // Only strong color assigned return e; } - // Generic scaling functions that refer to more than one material - // distribution. They should be probed after the specialized ones. - // Note that these ones don't return after setting the function. + // We didn't find any specialized scaling function, so fall back on generic + // ones that refer to more than one material distribution. Note that in this + // case we don't return after setting the function. if (is_KBPsKs(pos)) e->scalingFunction[WHITE] = &ScaleKBPsK[WHITE]; @@ -187,16 +182,18 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) { Value npm_w = pos.non_pawn_material(WHITE); Value npm_b = pos.non_pawn_material(BLACK); - if (npm_w + npm_b == VALUE_ZERO && pos.pieces(PAWN)) + if (npm_w + npm_b == VALUE_ZERO && pos.pieces(PAWN)) // Only pawns on the board { if (!pos.count(BLACK)) { assert(pos.count(WHITE) >= 2); + e->scalingFunction[WHITE] = &ScaleKPsK[WHITE]; } else if (!pos.count(WHITE)) { assert(pos.count(BLACK) >= 2); + e->scalingFunction[BLACK] = &ScaleKPsK[BLACK]; } else if (pos.count(WHITE) == 1 && pos.count(BLACK) == 1) @@ -208,14 +205,16 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) { } } - // No pawns makes it difficult to win, even with a material advantage. This - // catches some trivial draws like KK, KBK and KNK and gives a very drawish - // scale factor for cases such as KRKBP and KmmKm (except for KBBKN). + // Zero or just one pawn makes it difficult to win, even with a small material + // advantage. This catches some trivial draws like KK, KBK and KNK and gives a + // drawish scale factor for cases such as KRKBP and KmmKm (except for KBBKN). if (!pos.count(WHITE) && npm_w - npm_b <= BishopValueMg) - e->factor[WHITE] = uint8_t(npm_w < RookValueMg ? SCALE_FACTOR_DRAW : npm_b <= BishopValueMg ? 4 : 12); + e->factor[WHITE] = uint8_t(npm_w < RookValueMg ? SCALE_FACTOR_DRAW : + npm_b <= BishopValueMg ? 4 : 12); if (!pos.count(BLACK) && npm_b - npm_w <= BishopValueMg) - e->factor[BLACK] = uint8_t(npm_b < RookValueMg ? SCALE_FACTOR_DRAW : npm_w <= BishopValueMg ? 4 : 12); + e->factor[BLACK] = uint8_t(npm_b < RookValueMg ? SCALE_FACTOR_DRAW : + npm_w <= BishopValueMg ? 4 : 12); if (pos.count(WHITE) == 1 && npm_w - npm_b <= BishopValueMg) e->factor[WHITE] = (uint8_t) SCALE_FACTOR_ONEPAWN; @@ -223,25 +222,16 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) { if (pos.count(BLACK) == 1 && npm_b - npm_w <= BishopValueMg) e->factor[BLACK] = (uint8_t) SCALE_FACTOR_ONEPAWN; - // Compute the space weight - if (npm_w + npm_b >= 2 * QueenValueMg + 4 * RookValueMg + 2 * KnightValueMg) - { - int minorPieceCount = pos.count(WHITE) + pos.count(WHITE) - + pos.count(BLACK) + pos.count(BLACK); - - e->spaceWeight = make_score(minorPieceCount * minorPieceCount, 0); - } - // Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder // for the bishop pair "extended piece", which allows us to be more flexible // in defining bishop pair bonuses. - const int pieceCount[COLOR_NB][PIECE_TYPE_NB] = { + const int PieceCount[COLOR_NB][PIECE_TYPE_NB] = { { pos.count(WHITE) > 1, pos.count(WHITE), pos.count(WHITE), pos.count(WHITE) , pos.count(WHITE), pos.count(WHITE) }, { pos.count(BLACK) > 1, pos.count(BLACK), pos.count(BLACK), pos.count(BLACK) , pos.count(BLACK), pos.count(BLACK) } }; - e->value = (int16_t)((imbalance(pieceCount) - imbalance(pieceCount)) / 16); + e->value = int16_t((imbalance(PieceCount) - imbalance(PieceCount)) / 16); return e; } diff --git a/DroidFish/jni/stockfish/material.h b/DroidFish/jni/stockfish/material.h index f05541c..0119caa 100644 --- a/DroidFish/jni/stockfish/material.h +++ b/DroidFish/jni/stockfish/material.h @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -28,46 +28,44 @@ namespace Material { /// Material::Entry contains various information about a material configuration. -/// It contains a material balance evaluation, a function pointer to a special +/// It contains a material imbalance evaluation, a function pointer to a special /// endgame evaluation function (which in most cases is NULL, meaning that the -/// standard evaluation function will be used), and "scale factors". +/// standard evaluation function will be used), and scale factors. /// -/// The scale factors are used to scale the evaluation score up or down. -/// For instance, in KRB vs KR endgames, the score is scaled down by a factor -/// of 4, which will result in scores of absolute value less than one pawn. +/// The scale factors are used to scale the evaluation score up or down. For +/// instance, in KRB vs KR endgames, the score is scaled down by a factor of 4, +/// which will result in scores of absolute value less than one pawn. struct Entry { - Score material_value() const { return make_score(value, value); } - Score space_weight() const { return spaceWeight; } + Score imbalance() const { return make_score(value, value); } Phase game_phase() const { return gamePhase; } bool specialized_eval_exists() const { return evaluationFunction != NULL; } Value evaluate(const Position& pos) const { return (*evaluationFunction)(pos); } - // scale_factor takes a position and a color as input, and returns a scale factor - // for the given color. We have to provide the position in addition to the color, - // because the scale factor need not be a constant: It can also be a function - // which should be applied to the position. For instance, in KBP vs K endgames, - // a scaling function for draws with rook pawns and wrong-colored bishops. - + // scale_factor takes a position and a color as input and returns a scale factor + // for the given color. We have to provide the position in addition to the color + // because the scale factor may also be a function which should be applied to + // the position. For instance, in KBP vs K endgames, the scaling function looks + // for rook pawns and wrong-colored bishops. ScaleFactor scale_factor(const Position& pos, Color c) const { - - return !scalingFunction[c] || (*scalingFunction[c])(pos) == SCALE_FACTOR_NONE - ? ScaleFactor(factor[c]) : (*scalingFunction[c])(pos); + return !scalingFunction[c] + || (*scalingFunction[c])(pos) == SCALE_FACTOR_NONE ? ScaleFactor(factor[c]) + : (*scalingFunction[c])(pos); } Key key; int16_t value; uint8_t factor[COLOR_NB]; EndgameBase* evaluationFunction; - EndgameBase* scalingFunction[COLOR_NB]; - Score spaceWeight; + EndgameBase* scalingFunction[COLOR_NB]; // Could be one for each + // side (e.g. KPKP, KBPsKs) Phase gamePhase; }; typedef HashTable Table; -Entry* probe(const Position& pos, Table& entries, Endgames& endgames); +Entry* probe(const Position& pos); } // namespace Material diff --git a/DroidFish/jni/stockfish/misc.cpp b/DroidFish/jni/stockfish/misc.cpp index 3519611..59511db 100644 --- a/DroidFish/jni/stockfish/misc.cpp +++ b/DroidFish/jni/stockfish/misc.cpp @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -27,58 +27,14 @@ using namespace std; +namespace { + /// 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 = "121014"; - - -/// engine_info() returns the full name of the current Stockfish version. This -/// will be either "Stockfish DD-MM-YY" (where DD-MM-YY is the date when -/// the program was compiled) or "Stockfish ", depending on whether -/// Version is empty. - -const string engine_info(bool to_uci) { - - const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); - string month, day, year; - stringstream ss, date(__DATE__); // From compiler, format is "Sep 21 2008" - - ss << "Stockfish " << Version << setfill('0'); - - if (Version.empty()) - { - date >> month >> day >> year; - ss << setw(2) << day << setw(2) << (1 + months.find(month) / 4) << year.substr(2); - } - - ss << (Is64Bit ? " 64" : "") - << (HasPext ? " BMI2" : (HasPopCnt ? " SSE4.2" : "")) - << (to_uci ? "\nid author ": " by ") - << "Tord Romstad, Marco Costalba and Joona Kiiski"; - - return ss.str(); -} - - -/// Debug functions used mainly to collect run-time statistics - -static int64_t hits[2], means[2]; - -void dbg_hit_on(bool b) { ++hits[0]; if (b) ++hits[1]; } -void dbg_hit_on_c(bool c, bool b) { if (c) dbg_hit_on(b); } -void dbg_mean_of(int v) { ++means[0]; means[1] += v; } - -void dbg_print() { - - if (hits[0]) - cerr << "Total " << hits[0] << " Hits " << hits[1] - << " hit rate (%) " << 100 * hits[1] / hits[0] << endl; - - if (means[0]) - cerr << "Total " << means[0] << " Mean " - << (double)means[1] / means[0] << endl; -} +const string Version = "6"; +/// Debug counters +int64_t hits[2], means[2]; /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We @@ -137,6 +93,53 @@ public: } }; +} // namespace + +/// engine_info() returns the full name of the current Stockfish version. This +/// will be either "Stockfish DD-MM-YY" (where DD-MM-YY is the date when +/// the program was compiled) or "Stockfish ", depending on whether +/// Version is empty. + +const string engine_info(bool to_uci) { + + const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); + string month, day, year; + stringstream ss, date(__DATE__); // From compiler, format is "Sep 21 2008" + + ss << "Stockfish " << Version << setfill('0'); + + if (Version.empty()) + { + date >> month >> day >> year; + ss << setw(2) << day << setw(2) << (1 + months.find(month) / 4) << year.substr(2); + } + + ss << (Is64Bit ? " 64" : "") + << (HasPext ? " BMI2" : (HasPopCnt ? " POPCNT" : "")) + << (to_uci ? "\nid author ": " by ") + << "Tord Romstad, Marco Costalba and Joona Kiiski"; + + return ss.str(); +} + + +/// Debug functions used mainly to collect run-time statistics + +void dbg_hit_on(bool b) { ++hits[0]; if (b) ++hits[1]; } +void dbg_hit_on_c(bool c, bool b) { if (c) dbg_hit_on(b); } +void dbg_mean_of(int v) { ++means[0]; means[1] += v; } + +void dbg_print() { + + if (hits[0]) + cerr << "Total " << hits[0] << " Hits " << hits[1] + << " hit rate (%) " << 100 * hits[1] / hits[0] << endl; + + if (means[0]) + cerr << "Total " << means[0] << " Mean " + << (double)means[1] / means[0] << endl; +} + /// Used to serialize access to std::cout to avoid multiple threads writing at /// the same time. diff --git a/DroidFish/jni/stockfish/misc.h b/DroidFish/jni/stockfish/misc.h index 970f335..965a0ee 100644 --- a/DroidFish/jni/stockfish/misc.h +++ b/DroidFish/jni/stockfish/misc.h @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -20,21 +20,22 @@ #ifndef MISC_H_INCLUDED #define MISC_H_INCLUDED +#include #include #include #include #include "types.h" -extern const std::string engine_info(bool to_uci = false); -extern void timed_wait(WaitCondition&, Lock&, int); -extern void prefetch(char* addr); -extern void start_logger(bool b); +const std::string engine_info(bool to_uci = false); +void timed_wait(WaitCondition&, Lock&, int); +void prefetch(char* addr); +void start_logger(bool b); -extern void dbg_hit_on(bool b); -extern void dbg_hit_on_c(bool c, bool b); -extern void dbg_mean_of(int v); -extern void dbg_print(); +void dbg_hit_on(bool b); +void dbg_hit_on_c(bool c, bool b); +void dbg_mean_of(int v); +void dbg_print(); namespace Time { @@ -46,7 +47,7 @@ namespace Time { template struct HashTable { HashTable() : table(Size, Entry()) {} - Entry* operator[](Key k) { return &table[(uint32_t)k & (Size - 1)]; } + Entry* operator[](Key key) { return &table[(uint32_t)key & (Size - 1)]; } private: std::vector table; @@ -59,4 +60,41 @@ std::ostream& operator<<(std::ostream&, SyncCout); #define sync_cout std::cout << IO_LOCK #define sync_endl std::endl << IO_UNLOCK + +/// xorshift64star Pseudo-Random Number Generator +/// This class is based on original code written and dedicated +/// to the public domain by Sebastiano Vigna (2014). +/// It has the following characteristics: +/// +/// - Outputs 64-bit numbers +/// - Passes Dieharder and SmallCrush test batteries +/// - Does not require warm-up, no zeroland to escape +/// - Internal state is a single 64-bit integer +/// - Period is 2^64 - 1 +/// - Speed: 1.60 ns/call (Core i7 @3.40GHz) +/// +/// For further analysis see +/// + +class PRNG { + + uint64_t s; + + uint64_t rand64() { + + s ^= s >> 12, s ^= s << 25, s ^= s >> 27; + return s * 2685821657736338717LL; + } + +public: + PRNG(uint64_t seed) : s(seed) { assert(seed); } + + template T rand() { return T(rand64()); } + + /// Special generator used to fast init magic numbers. + /// Output values only have 1/8th of their bits set on average. + template T sparse_rand() + { return T(rand64() & rand64() & rand64()); } +}; + #endif // #ifndef MISC_H_INCLUDED diff --git a/DroidFish/jni/stockfish/movegen.cpp b/DroidFish/jni/stockfish/movegen.cpp index dab830b..bfc8858 100644 --- a/DroidFish/jni/stockfish/movegen.cpp +++ b/DroidFish/jni/stockfish/movegen.cpp @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -25,12 +25,12 @@ namespace { template - ExtMove* generate_castling(const Position& pos, ExtMove* mlist, Color us, const CheckInfo* ci) { + ExtMove* generate_castling(const Position& pos, ExtMove* moveList, Color us, const CheckInfo* ci) { static const bool KingSide = (Cr == WHITE_OO || Cr == BLACK_OO); if (pos.castling_impeded(Cr) || !pos.can_castle(Cr)) - return mlist; + return moveList; // After castling, the rook and king final positions are the same in Chess960 // as they would be in standard chess. @@ -46,59 +46,51 @@ namespace { for (Square s = kto; s != kfrom; s += K) if (pos.attackers_to(s) & enemies) - return mlist; + return moveList; // Because we generate only legal castling moves we need to verify that // when moving the castling rook we do not discover some hidden checker. // For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1. if (Chess960 && (attacks_bb(kto, pos.pieces() ^ rfrom) & pos.pieces(~us, ROOK, QUEEN))) - return mlist; + return moveList; Move m = make(kfrom, rfrom); if (Checks && !pos.gives_check(m, *ci)) - return mlist; + return moveList; - (mlist++)->move = m; + (moveList++)->move = m; - return mlist; + return moveList; } template - inline ExtMove* generate_promotions(ExtMove* mlist, Bitboard pawnsOn7, - Bitboard target, const CheckInfo* ci) { + inline ExtMove* make_promotions(ExtMove* moveList, Square to, const CheckInfo* ci) { - Bitboard b = shift_bb(pawnsOn7) & target; + if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) + (moveList++)->move = make(to - Delta, to, QUEEN); - while (b) + if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS) { - Square to = pop_lsb(&b); - - if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) - (mlist++)->move = make(to - Delta, to, QUEEN); - - if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS) - { - (mlist++)->move = make(to - Delta, to, ROOK); - (mlist++)->move = make(to - Delta, to, BISHOP); - (mlist++)->move = make(to - Delta, to, KNIGHT); - } - - // Knight promotion is the only promotion that can give a direct check - // that's not already included in the queen promotion. - if (Type == QUIET_CHECKS && (StepAttacksBB[W_KNIGHT][to] & ci->ksq)) - (mlist++)->move = make(to - Delta, to, KNIGHT); - else - (void)ci; // Silence a warning under MSVC + (moveList++)->move = make(to - Delta, to, ROOK); + (moveList++)->move = make(to - Delta, to, BISHOP); + (moveList++)->move = make(to - Delta, to, KNIGHT); } - return mlist; + // Knight promotion is the only promotion that can give a direct check + // that's not already included in the queen promotion. + if (Type == QUIET_CHECKS && (StepAttacksBB[W_KNIGHT][to] & ci->ksq)) + (moveList++)->move = make(to - Delta, to, KNIGHT); + else + (void)ci; // Silence a warning under MSVC + + return moveList; } template - ExtMove* generate_pawn_moves(const Position& pos, ExtMove* mlist, + ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard target, const CheckInfo* ci) { // Compute our parametrized parameters at compile time, named according to @@ -111,7 +103,7 @@ namespace { const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW); const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE); - Bitboard b1, b2, dc1, dc2, emptySquares; + Bitboard emptySquares; Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB; Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB; @@ -124,8 +116,8 @@ namespace { { emptySquares = (Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces()); - b1 = shift_bb(pawnsNotOn7) & emptySquares; - b2 = shift_bb(b1 & TRank3BB) & emptySquares; + Bitboard b1 = shift_bb(pawnsNotOn7) & emptySquares; + Bitboard b2 = shift_bb(b1 & TRank3BB) & emptySquares; if (Type == EVASIONS) // Consider only blocking squares { @@ -144,8 +136,8 @@ namespace { // promotion has been already generated amongst the captures. if (pawnsNotOn7 & ci->dcCandidates) { - dc1 = shift_bb(pawnsNotOn7 & ci->dcCandidates) & emptySquares & ~file_bb(ci->ksq); - dc2 = shift_bb(dc1 & TRank3BB) & emptySquares; + Bitboard dc1 = shift_bb(pawnsNotOn7 & ci->dcCandidates) & emptySquares & ~file_bb(ci->ksq); + Bitboard dc2 = shift_bb(dc1 & TRank3BB) & emptySquares; b1 |= dc1; b2 |= dc2; @@ -155,13 +147,13 @@ namespace { while (b1) { Square to = pop_lsb(&b1); - (mlist++)->move = make_move(to - Up, to); + (moveList++)->move = make_move(to - Up, to); } while (b2) { Square to = pop_lsb(&b2); - (mlist++)->move = make_move(to - Up - Up, to); + (moveList++)->move = make_move(to - Up - Up, to); } } @@ -174,27 +166,36 @@ namespace { if (Type == EVASIONS) emptySquares &= target; - mlist = generate_promotions(mlist, pawnsOn7, enemies, ci); - mlist = generate_promotions(mlist, pawnsOn7, enemies, ci); - mlist = generate_promotions(mlist, pawnsOn7, emptySquares, ci); + Bitboard b1 = shift_bb(pawnsOn7) & enemies; + Bitboard b2 = shift_bb(pawnsOn7) & enemies; + Bitboard b3 = shift_bb(pawnsOn7) & emptySquares; + + while (b1) + moveList = make_promotions(moveList, pop_lsb(&b1), ci); + + while (b2) + moveList = make_promotions(moveList, pop_lsb(&b2), ci); + + while (b3) + moveList = make_promotions(moveList, pop_lsb(&b3), ci); } // Standard and en-passant captures if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) { - b1 = shift_bb(pawnsNotOn7) & enemies; - b2 = shift_bb(pawnsNotOn7) & enemies; + Bitboard b1 = shift_bb(pawnsNotOn7) & enemies; + Bitboard b2 = shift_bb(pawnsNotOn7) & enemies; while (b1) { Square to = pop_lsb(&b1); - (mlist++)->move = make_move(to - Right, to); + (moveList++)->move = make_move(to - Right, to); } while (b2) { Square to = pop_lsb(&b2); - (mlist++)->move = make_move(to - Left, to); + (moveList++)->move = make_move(to - Left, to); } if (pos.ep_square() != SQ_NONE) @@ -205,23 +206,23 @@ namespace { // is the double pushed pawn and so is in the target. Otherwise this // is a discovery check and we are forced to do otherwise. if (Type == EVASIONS && !(target & (pos.ep_square() - Up))) - return mlist; + return moveList; b1 = pawnsNotOn7 & pos.attacks_from(pos.ep_square(), Them); assert(b1); while (b1) - (mlist++)->move = make(pop_lsb(&b1), pos.ep_square()); + (moveList++)->move = make(pop_lsb(&b1), pos.ep_square()); } } - return mlist; + return moveList; } template FORCE_INLINE - ExtMove* generate_moves(const Position& pos, ExtMove* mlist, Color us, + ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Color us, Bitboard target, const CheckInfo* ci) { assert(Pt != KING && Pt != PAWN); @@ -236,7 +237,7 @@ namespace { && !(PseudoAttacks[Pt][from] & target & ci->checkSq[Pt])) continue; - if (unlikely(ci->dcCandidates) && (ci->dcCandidates & from)) + if (ci->dcCandidates && (ci->dcCandidates & from)) continue; } @@ -246,51 +247,50 @@ namespace { b &= ci->checkSq[Pt]; while (b) - (mlist++)->move = make_move(from, pop_lsb(&b)); + (moveList++)->move = make_move(from, pop_lsb(&b)); } - return mlist; + return moveList; } template FORCE_INLINE - ExtMove* generate_all(const Position& pos, ExtMove* mlist, Bitboard target, + ExtMove* generate_all(const Position& pos, ExtMove* moveList, Bitboard target, const CheckInfo* ci = NULL) { const bool Checks = Type == QUIET_CHECKS; - mlist = generate_pawn_moves(pos, mlist, target, ci); - mlist = generate_moves(pos, mlist, Us, target, ci); - mlist = generate_moves(pos, mlist, Us, target, ci); - mlist = generate_moves< ROOK, Checks>(pos, mlist, Us, target, ci); - mlist = generate_moves< QUEEN, Checks>(pos, mlist, Us, target, ci); + moveList = generate_pawn_moves(pos, moveList, target, ci); + moveList = generate_moves(pos, moveList, Us, target, ci); + moveList = generate_moves(pos, moveList, Us, target, ci); + moveList = generate_moves< ROOK, Checks>(pos, moveList, Us, target, ci); + moveList = generate_moves< QUEEN, Checks>(pos, moveList, Us, target, ci); if (Type != QUIET_CHECKS && Type != EVASIONS) { Square ksq = pos.king_square(Us); Bitboard b = pos.attacks_from(ksq) & target; while (b) - (mlist++)->move = make_move(ksq, pop_lsb(&b)); + (moveList++)->move = make_move(ksq, pop_lsb(&b)); } if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(Us)) { if (pos.is_chess960()) { - mlist = generate_castling::right, Checks, true>(pos, mlist, Us, ci); - mlist = generate_castling::right, Checks, true>(pos, mlist, Us, ci); + moveList = generate_castling::right, Checks, true>(pos, moveList, Us, ci); + moveList = generate_castling::right, Checks, true>(pos, moveList, Us, ci); } else { - mlist = generate_castling::right, Checks, false>(pos, mlist, Us, ci); - mlist = generate_castling::right, Checks, false>(pos, mlist, Us, ci); + moveList = generate_castling::right, Checks, false>(pos, moveList, Us, ci); + moveList = generate_castling::right, Checks, false>(pos, moveList, Us, ci); } } - return mlist; + return moveList; } - } // namespace @@ -304,19 +304,19 @@ namespace { /// non-captures. Returns a pointer to the end of the move list. template -ExtMove* generate(const Position& pos, ExtMove* mlist) { +ExtMove* generate(const Position& pos, ExtMove* moveList) { assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS); assert(!pos.checkers()); Color us = pos.side_to_move(); - Bitboard target = Type == CAPTURES ? pos.pieces(~us) - : Type == QUIETS ? ~pos.pieces() - : Type == NON_EVASIONS ? ~pos.pieces(us) : 0; + Bitboard target = Type == CAPTURES ? pos.pieces(~us) + : Type == QUIETS ? ~pos.pieces() + : Type == NON_EVASIONS ? ~pos.pieces(us) : 0; - return us == WHITE ? generate_all(pos, mlist, target) - : generate_all(pos, mlist, target); + return us == WHITE ? generate_all(pos, moveList, target) + : generate_all(pos, moveList, target); } // Explicit template instantiations @@ -328,7 +328,7 @@ template ExtMove* generate(const Position&, ExtMove*); /// generate generates all pseudo-legal non-captures and knight /// underpromotions that give check. Returns a pointer to the end of the move list. template<> -ExtMove* generate(const Position& pos, ExtMove* mlist) { +ExtMove* generate(const Position& pos, ExtMove* moveList) { assert(!pos.checkers()); @@ -350,18 +350,18 @@ ExtMove* generate(const Position& pos, ExtMove* mlist) { b &= ~PseudoAttacks[QUEEN][ci.ksq]; while (b) - (mlist++)->move = make_move(from, pop_lsb(&b)); + (moveList++)->move = make_move(from, pop_lsb(&b)); } - return us == WHITE ? generate_all(pos, mlist, ~pos.pieces(), &ci) - : generate_all(pos, mlist, ~pos.pieces(), &ci); + return us == WHITE ? generate_all(pos, moveList, ~pos.pieces(), &ci) + : generate_all(pos, moveList, ~pos.pieces(), &ci); } /// generate generates all pseudo-legal check evasions when the side /// to move is in check. Returns a pointer to the end of the move list. template<> -ExtMove* generate(const Position& pos, ExtMove* mlist) { +ExtMove* generate(const Position& pos, ExtMove* moveList) { assert(pos.checkers()); @@ -382,37 +382,37 @@ ExtMove* generate(const Position& pos, ExtMove* mlist) { // Generate evasions for king, capture and non capture moves Bitboard b = pos.attacks_from(ksq) & ~pos.pieces(us) & ~sliderAttacks; while (b) - (mlist++)->move = make_move(ksq, pop_lsb(&b)); + (moveList++)->move = make_move(ksq, pop_lsb(&b)); if (more_than_one(pos.checkers())) - return mlist; // Double check, only a king move can save the day + return moveList; // Double check, only a king move can save the day // Generate blocking evasions or captures of the checking piece Square checksq = lsb(pos.checkers()); Bitboard target = between_bb(checksq, ksq) | checksq; - return us == WHITE ? generate_all(pos, mlist, target) - : generate_all(pos, mlist, target); + return us == WHITE ? generate_all(pos, moveList, target) + : generate_all(pos, moveList, target); } /// generate generates all the legal moves in the given position template<> -ExtMove* generate(const Position& pos, ExtMove* mlist) { +ExtMove* generate(const Position& pos, ExtMove* moveList) { - ExtMove *end, *cur = mlist; Bitboard pinned = pos.pinned_pieces(pos.side_to_move()); Square ksq = pos.king_square(pos.side_to_move()); + ExtMove* cur = moveList; - end = pos.checkers() ? generate(pos, mlist) - : generate(pos, mlist); - while (cur != end) + moveList = pos.checkers() ? generate(pos, moveList) + : generate(pos, moveList); + while (cur != moveList) if ( (pinned || from_sq(cur->move) == ksq || type_of(cur->move) == ENPASSANT) && !pos.legal(cur->move, pinned)) - cur->move = (--end)->move; + cur->move = (--moveList)->move; else ++cur; - return end; + return moveList; } diff --git a/DroidFish/jni/stockfish/movegen.h b/DroidFish/jni/stockfish/movegen.h index c18fa07..62a121d 100644 --- a/DroidFish/jni/stockfish/movegen.h +++ b/DroidFish/jni/stockfish/movegen.h @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -22,6 +22,8 @@ #include "types.h" +class Position; + enum GenType { CAPTURES, QUIETS, @@ -31,27 +33,34 @@ enum GenType { LEGAL }; -class Position; +struct ExtMove { + Move move; + Value value; +}; + +inline bool operator<(const ExtMove& f, const ExtMove& s) { + return f.value < s.value; +} template -ExtMove* generate(const Position& pos, ExtMove* mlist); +ExtMove* generate(const Position& pos, ExtMove* moveList); /// The MoveList struct is a simple wrapper around generate(). It sometimes comes /// in handy to use this class instead of the low level generate() function. template struct MoveList { - explicit MoveList(const Position& pos) : cur(mlist), last(generate(pos, mlist)) { last->move = MOVE_NONE; } + explicit MoveList(const Position& pos) : cur(moveList), last(generate(pos, moveList)) { last->move = MOVE_NONE; } void operator++() { ++cur; } Move operator*() const { return cur->move; } - size_t size() const { return last - mlist; } + size_t size() const { return last - moveList; } bool contains(Move m) const { - for (const ExtMove* it(mlist); it != last; ++it) if (it->move == m) return true; + for (const ExtMove* it(moveList); it != last; ++it) if (it->move == m) return true; return false; } private: - ExtMove mlist[MAX_MOVES]; + ExtMove moveList[MAX_MOVES]; ExtMove *cur, *last; }; diff --git a/DroidFish/jni/stockfish/movepick.cpp b/DroidFish/jni/stockfish/movepick.cpp index 95172b9..edee817 100644 --- a/DroidFish/jni/stockfish/movepick.cpp +++ b/DroidFish/jni/stockfish/movepick.cpp @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -51,7 +51,7 @@ namespace { // Unary predicate used by std::partition to split positive values from remaining // ones so as to sort the two sets separately, with the second sort delayed. - inline bool has_positive_value(const ExtMove& ms) { return ms.value > 0; } + inline bool has_positive_value(const ExtMove& move) { return move.value > VALUE_ZERO; } // Picks the best move in the range (begin, end) and moves it to the front. // It's faster than sorting all the moves in advance when there are few @@ -61,7 +61,7 @@ namespace { std::swap(*begin, *std::max_element(begin, end)); return begin; } -} +} // namespace /// Constructors of the MovePicker class. As arguments we pass information @@ -103,15 +103,8 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats& stage = QSEARCH_0; else if (d > DEPTH_QS_RECAPTURES) - { stage = QSEARCH_1; - // Skip TT move if is not a capture or a promotion. This avoids qsearch - // tree explosion due to a possible perpetual check or similar rare cases - // when TT table is full. - if (ttm && !pos.capture_or_promotion(ttm)) - ttm = MOVE_NONE; - } else { stage = RECAPTURE; diff --git a/DroidFish/jni/stockfish/movepick.h b/DroidFish/jni/stockfish/movepick.h index c512d13..a7b25a9 100644 --- a/DroidFish/jni/stockfish/movepick.h +++ b/DroidFish/jni/stockfish/movepick.h @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -40,7 +40,7 @@ template struct Stats { - static const Value Max = Value(2000); + static const Value Max = Value(250); const T* operator[](Piece pc) const { return table[pc]; } void clear() { std::memset(table, 0, sizeof(table)); } diff --git a/DroidFish/jni/stockfish/notation.cpp b/DroidFish/jni/stockfish/notation.cpp deleted file mode 100644 index 7e8765f..0000000 --- a/DroidFish/jni/stockfish/notation.cpp +++ /dev/null @@ -1,95 +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 . -*/ - -#include -#include - -#include "movegen.h" -#include "notation.h" -#include "position.h" - -using namespace std; - -static const char* PieceToChar[COLOR_NB] = { " PNBRQK", " pnbrqk" }; - - -/// score_to_uci() converts a value to a string suitable for use with the UCI -/// protocol specifications: -/// -/// cp The score from the engine's point of view in centipawns. -/// mate Mate in y moves, not plies. If the engine is getting mated -/// use negative values for y. - -string score_to_uci(Value v, Value alpha, Value beta) { - - stringstream ss; - - 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; - - ss << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : ""); - - return ss.str(); -} - - -/// move_to_uci() converts a move to a string in coordinate notation -/// (g1f3, a7a8q, etc.). The only special case is castling moves, where we print -/// in the e1g1 notation in normal chess mode, and in e1h1 notation in chess960 -/// mode. Internally castling moves are always encoded as "king captures rook". - -const string move_to_uci(Move m, bool chess960) { - - Square from = from_sq(m); - Square to = to_sq(m); - - if (m == MOVE_NONE) - return "(none)"; - - if (m == MOVE_NULL) - return "0000"; - - if (type_of(m) == CASTLING && !chess960) - to = make_square(to > from ? FILE_G : FILE_C, rank_of(from)); - - string move = to_string(from) + to_string(to); - - if (type_of(m) == PROMOTION) - move += PieceToChar[BLACK][promotion_type(m)]; // Lower case - - return move; -} - - -/// move_from_uci() takes a position and a string representing a move in -/// simple coordinate notation and returns an equivalent legal Move if any. - -Move move_from_uci(const Position& pos, string& str) { - - if (str.length() == 5) // Junior could send promotion piece in uppercase - str[4] = char(tolower(str[4])); - - for (MoveList it(pos); *it; ++it) - if (str == move_to_uci(*it, pos.is_chess960())) - return *it; - - return MOVE_NONE; -} diff --git a/DroidFish/jni/stockfish/notation.h b/DroidFish/jni/stockfish/notation.h deleted file mode 100644 index 251a0ca..0000000 --- a/DroidFish/jni/stockfish/notation.h +++ /dev/null @@ -1,46 +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 NOTATION_H_INCLUDED -#define NOTATION_H_INCLUDED - -#include - -#include "types.h" - -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); - -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 4693e15..336638e 100644 --- a/DroidFish/jni/stockfish/pawns.cpp +++ b/DroidFish/jni/stockfish/pawns.cpp @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -24,6 +24,7 @@ #include "bitcount.h" #include "pawns.h" #include "position.h" +#include "thread.h" namespace { @@ -49,34 +50,46 @@ 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 bonus by rank - const int Connected[RANK_NB] = {0, 6, 15, 10, 57, 75, 135, 258}; + // Connected pawn bonus by opposed, phalanx flags and rank + Score Connected[2][2][RANK_NB]; // 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); - // Unsupported pawn penalty const Score UnsupportedPawnPenalty = S(20, 10); - // Weakness of our pawn shelter in front of the king indexed by [rank] - const Value ShelterWeakness[RANK_NB] = - { V(100), V(0), V(27), V(73), V(92), V(101), V(101) }; + // Weakness of our pawn shelter in front of the king by [distance from edge][rank] + const Value ShelterWeakness[][RANK_NB] = { + { V(100), V(13), V(24), V(64), V(89), V( 93), V(104) }, + { V(110), V( 1), V(29), V(75), V(96), V(102), V(107) }, + { V(102), V( 0), V(39), V(74), V(88), V(101), V( 98) }, + { V( 88), V( 4), V(33), V(67), V(92), V( 94), V(107) } }; - // Danger of enemy pawns moving toward our king indexed by - // [no friendly pawn | pawn unblocked | pawn blocked][rank of enemy pawn] - 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) } }; + // Danger of enemy pawns moving toward our king by [type][distance from edge][rank] + const Value StormDanger[][4][RANK_NB] = { + { { V( 0), V( 63), V( 128), V(43), V(27) }, + { V( 0), V( 62), V( 131), V(44), V(26) }, + { V( 0), V( 59), V( 121), V(50), V(28) }, + { V( 0), V( 62), V( 127), V(54), V(28) } }, + { { V(24), V( 40), V( 93), V(42), V(22) }, + { V(24), V( 28), V( 101), V(38), V(20) }, + { V(24), V( 32), V( 95), V(36), V(23) }, + { V(27), V( 24), V( 99), V(36), V(24) } }, + { { V( 0), V( 0), V( 81), V(16), V( 6) }, + { V( 0), V( 0), V( 165), V(29), V( 9) }, + { V( 0), V( 0), V( 163), V(23), V(12) }, + { V( 0), V( 0), V( 161), V(28), V(13) } }, + { { V( 0), V(-296), V(-299), V(55), V(25) }, + { V( 0), V( 67), V( 131), V(46), V(21) }, + { V( 0), V( 65), V( 135), V(50), V(31) }, + { V( 0), V( 62), V( 128), V(51), V(24) } } }; // Max bonus for king safety. Corresponds to start position with all the pawns // in front of the king and no enemy pawn on the horizon. - const Value MaxSafetyBonus = V(263); + const Value MaxSafetyBonus = V(257); #undef S #undef V @@ -89,10 +102,10 @@ namespace { const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW); const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE); - Bitboard b, p, doubled; + Bitboard b, p, doubled, connected; Square s; - bool passed, isolated, opposed, connected, backward, unsupported, lever; - Score value = SCORE_ZERO; + bool passed, isolated, opposed, phalanx, backward, unsupported, lever; + Score score = SCORE_ZERO; const Square* pl = pos.list(Us); const Bitboard* pawnAttacksBB = StepAttacksBB[make_piece(Us, PAWN)]; @@ -111,7 +124,6 @@ namespace { { assert(pos.piece_on(s) == make_piece(Us, PAWN)); - Rank r = rank_of(s), rr = relative_rank(Us, s); File f = file_of(s); // This file cannot be semi-open @@ -120,12 +132,10 @@ namespace { // Previous rank p = rank_bb(s - pawn_push(Us)); - // Our rank plus previous one - b = rank_bb(s) | p; - // Flag the pawn as passed, isolated, doubled, // unsupported or connected (but not the backward one). - connected = ourPawns & adjacent_files_bb(f) & b; + connected = ourPawns & adjacent_files_bb(f) & (rank_bb(s) | p); + phalanx = connected & rank_bb(s); unsupported = !(ourPawns & adjacent_files_bb(f) & p); isolated = !(ourPawns & adjacent_files_bb(f)); doubled = ourPawns & forward_bb(Us, s); @@ -165,56 +175,67 @@ namespace { // Score this pawn if (isolated) - value -= Isolated[opposed][f]; + score -= Isolated[opposed][f]; if (unsupported && !isolated) - value -= UnsupportedPawnPenalty; + score -= UnsupportedPawnPenalty; if (doubled) - value -= Doubled[f] / rank_distance(s, lsb(doubled)); + score -= Doubled[f] / distance(s, frontmost_sq(Us, doubled)); if (backward) - value -= Backward[opposed][f]; + score -= Backward[opposed][f]; - 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 (connected) + score += Connected[opposed][phalanx][relative_rank(Us, s)]; if (lever) - value += Lever[rr]; + score += Lever[relative_rank(Us, s)]; } 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. - value += PawnsFileSpan * e->pawnSpan[Us]; - - return value; + return score; } } // namespace namespace Pawns { -/// 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. +/// Pawns::init() initializes some tables needed by evaluation. Instead of using +/// hard-coded tables, when makes sense, we prefer to calculate them with a formula +/// to reduce independent parameters and to allow easier tuning and better insight. -Entry* probe(const Position& pos, Table& entries) { +void init() +{ + static const int Seed[RANK_NB] = { 0, 6, 15, 10, 57, 75, 135, 258 }; + + for (int opposed = 0; opposed <= 1; ++opposed) + for (int phalanx = 0; phalanx <= 1; ++phalanx) + for (Rank r = RANK_2; r < RANK_8; ++r) + { + int bonus = Seed[r] + (phalanx ? (Seed[r + 1] - Seed[r]) / 2 : 0); + Connected[opposed][phalanx][r] = make_score(bonus / 2, bonus >> opposed); + } +} + + +/// Pawns::probe() looks up the current position's pawns configuration in +/// the pawns hash table. It returns a pointer to the Entry if the position +/// is found. Otherwise a new Entry is computed and stored there, so we don't +/// have to recompute all when the same pawns configuration occurs again. + +Entry* probe(const Position& pos) { Key key = pos.pawn_key(); - Entry* e = entries[key]; + Entry* e = pos.this_thread()->pawnsTable[key]; if (e->key == key) return e; e->key = key; - e->value = evaluate(pos, e) - evaluate(pos, e); + e->score = evaluate(pos, e) - evaluate(pos, e); return e; } @@ -226,15 +247,16 @@ template Value Entry::shelter_storm(const Position& pos, Square ksq) { const Color Them = (Us == WHITE ? BLACK : WHITE); - const Bitboard Edges = (FileABB | FileHBB) & (Rank2BB | Rank3BB); + + enum { NoFriendlyPawn, Unblocked, BlockedByPawn, BlockedByKing }; 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); Value safety = MaxSafetyBonus; - File kf = std::max(FILE_B, std::min(FILE_G, file_of(ksq))); + File center = std::max(FILE_B, std::min(FILE_G, file_of(ksq))); - for (File f = kf - File(1); f <= kf + File(1); ++f) + for (File f = center - File(1); f <= center + File(1); ++f) { b = ourPawns & file_bb(f); Rank rkUs = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1; @@ -242,14 +264,12 @@ Value Entry::shelter_storm(const Position& pos, Square ksq) { b = theirPawns & file_bb(f); Rank rkThem = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1; - 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 ? 1 : 2][rkThem]; + safety -= ShelterWeakness[std::min(f, FILE_H - f)][rkUs] + + StormDanger + [f == file_of(ksq) && rkThem == relative_rank(Us, ksq) + 1 ? BlockedByKing : + rkUs == RANK_1 ? NoFriendlyPawn : + rkThem == rkUs + 1 ? BlockedByPawn : Unblocked] + [std::min(f, FILE_H - f)][rkThem]; } return safety; @@ -264,14 +284,14 @@ Score Entry::do_king_safety(const Position& pos, Square ksq) { kingSquares[Us] = ksq; castlingRights[Us] = pos.can_castle(Us); - minKPdistance[Us] = 0; + minKingPawnDistance[Us] = 0; Bitboard pawns = pos.pieces(Us, PAWN); if (pawns) - while (!(DistanceRingsBB[ksq][minKPdistance[Us]++] & pawns)) {} + while (!(DistanceRingBB[ksq][minKingPawnDistance[Us]++] & pawns)) {} if (relative_rank(Us, ksq) > RANK_4) - return make_score(0, -16 * minKPdistance[Us]); + return make_score(0, -16 * minKingPawnDistance[Us]); Value bonus = shelter_storm(pos, ksq); @@ -282,7 +302,7 @@ Score Entry::do_king_safety(const Position& pos, Square ksq) { if (pos.can_castle(MakeCastling::right)) bonus = std::max(bonus, shelter_storm(pos, relative_square(Us, SQ_C1))); - return make_score(bonus, -16 * minKPdistance[Us]); + return make_score(bonus, -16 * minKingPawnDistance[Us]); } // Explicit template instantiation diff --git a/DroidFish/jni/stockfish/pawns.h b/DroidFish/jni/stockfish/pawns.h index f24fb75..df38d55 100644 --- a/DroidFish/jni/stockfish/pawns.h +++ b/DroidFish/jni/stockfish/pawns.h @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -32,9 +32,10 @@ namespace Pawns { struct Entry { - Score pawns_value() const { return value; } + Score pawns_score() const { return score; } Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; } Bitboard passed_pawns(Color c) const { return passedPawns[c]; } + int pawn_span(Color c) const { return pawnSpan[c]; } int semiopen_file(Color c, File f) const { return semiopenFiles[c] & (1 << f); @@ -44,10 +45,6 @@ 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)]; } @@ -65,12 +62,12 @@ struct Entry { Value shelter_storm(const Position& pos, Square ksq); Key key; - Score value; + Score score; Bitboard passedPawns[COLOR_NB]; Bitboard pawnAttacks[COLOR_NB]; Square kingSquares[COLOR_NB]; Score kingSafety[COLOR_NB]; - int minKPdistance[COLOR_NB]; + int minKingPawnDistance[COLOR_NB]; int castlingRights[COLOR_NB]; int semiopenFiles[COLOR_NB]; int pawnSpan[COLOR_NB]; @@ -79,7 +76,8 @@ struct Entry { typedef HashTable Table; -Entry* probe(const Position& pos, Table& entries); +void init(); +Entry* probe(const Position& pos); } // namespace Pawns diff --git a/DroidFish/jni/stockfish/platform.h b/DroidFish/jni/stockfish/platform.h index 151c882..47c0b11 100644 --- a/DroidFish/jni/stockfish/platform.h +++ b/DroidFish/jni/stockfish/platform.h @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 diff --git a/DroidFish/jni/stockfish/position.cpp b/DroidFish/jni/stockfish/position.cpp index cbaa07e..994e1c7 100644 --- a/DroidFish/jni/stockfish/position.cpp +++ b/DroidFish/jni/stockfish/position.cpp @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -19,31 +19,25 @@ #include #include -#include +#include // For std::memset #include #include #include "bitcount.h" +#include "misc.h" #include "movegen.h" -#include "notation.h" #include "position.h" #include "psqtab.h" -#include "rkiss.h" #include "thread.h" #include "tt.h" +#include "uci.h" using std::string; -static const string PieceToChar(" PNBRQK pnbrqk"); - -CACHE_LINE_ALIGNMENT - Value PieceValue[PHASE_NB][PIECE_NB] = { { VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg }, { VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } }; -static Score psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; - namespace Zobrist { Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; @@ -57,6 +51,9 @@ Key Position::exclusion_key() const { return st->key ^ Zobrist::exclusion;} namespace { +const string PieceToChar(" PNBRQK pnbrqk"); +Score psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; + // min_attacker() is a helper function used by see() to locate the least // valuable attacker for the side to move, remove the attacker we just found // from the bitboards and scan for new X-ray attacks behind it. @@ -108,6 +105,30 @@ CheckInfo::CheckInfo(const Position& pos) { } +/// operator<<(Position) returns an ASCII representation of the position + +std::ostream& operator<<(std::ostream& os, const Position& pos) { + + os << "\n +---+---+---+---+---+---+---+---+\n"; + + for (Rank r = RANK_8; r >= RANK_1; --r) + { + for (File f = FILE_A; f <= FILE_H; ++f) + os << " | " << PieceToChar[pos.piece_on(make_square(f, r))]; + + os << " |\n +---+---+---+---+---+---+---+---+\n"; + } + + os << "\nFen: " << pos.fen() << "\nKey: " << std::hex << std::uppercase + << std::setfill('0') << std::setw(16) << pos.st->key << std::dec << "\nCheckers: "; + + for (Bitboard b = pos.checkers(); b; ) + os << UCI::square(pop_lsb(&b)) << " "; + + return os; +} + + /// Position::init() initializes at startup the various arrays used to compute /// hash keys and the piece square tables. The latter is a two-step operation: /// Firstly, the white halves of the tables are copied from PSQT[] tables. @@ -116,28 +137,28 @@ CheckInfo::CheckInfo(const Position& pos) { void Position::init() { - RKISS rk; + PRNG rng(1070372); for (Color c = WHITE; c <= BLACK; ++c) for (PieceType pt = PAWN; pt <= KING; ++pt) for (Square s = SQ_A1; s <= SQ_H8; ++s) - Zobrist::psq[c][pt][s] = rk.rand(); + Zobrist::psq[c][pt][s] = rng.rand(); for (File f = FILE_A; f <= FILE_H; ++f) - Zobrist::enpassant[f] = rk.rand(); + Zobrist::enpassant[f] = rng.rand(); - for (int cf = NO_CASTLING; cf <= ANY_CASTLING; ++cf) + for (int cr = NO_CASTLING; cr <= ANY_CASTLING; ++cr) { - Bitboard b = cf; + Bitboard b = cr; while (b) { Key k = Zobrist::castling[1ULL << pop_lsb(&b)]; - Zobrist::castling[cf] ^= k ? k : rk.rand(); + Zobrist::castling[cr] ^= k ? k : rng.rand(); } } - Zobrist::side = rk.rand(); - Zobrist::exclusion = rk.rand(); + Zobrist::side = rng.rand(); + Zobrist::exclusion = rng.rand(); for (PieceType pt = PAWN; pt <= KING; ++pt) { @@ -155,9 +176,8 @@ void Position::init() { } -/// Position::operator=() creates a copy of 'pos'. We want the new born Position -/// object to not depend on any external data so we detach state pointer from -/// the source one. +/// Position::operator=() creates a copy of 'pos' but detaching the state pointer +/// from the source to be self-consistent and not depending on any external data. Position& Position::operator=(const Position& pos) { @@ -342,7 +362,7 @@ void Position::set_castling_right(Color c, Square rfrom) { void Position::set_state(StateInfo* si) const { si->key = si->pawnKey = si->materialKey = 0; - si->npMaterial[WHITE] = si->npMaterial[BLACK] = VALUE_ZERO; + si->nonPawnMaterial[WHITE] = si->nonPawnMaterial[BLACK] = VALUE_ZERO; si->psq = SCORE_ZERO; si->checkersBB = attackers_to(king_square(sideToMove)) & pieces(~sideToMove); @@ -376,7 +396,7 @@ void Position::set_state(StateInfo* si) const { for (Color c = WHITE; c <= BLACK; ++c) for (PieceType pt = KNIGHT; pt <= QUEEN; ++pt) - si->npMaterial[c] += pieceCount[c][pt] * PieceValue[MG][pt]; + si->nonPawnMaterial[c] += pieceCount[c][pt] * PieceValue[MG][pt]; } @@ -409,63 +429,37 @@ const string Position::fen() const { ss << (sideToMove == WHITE ? " w " : " b "); if (can_castle(WHITE_OO)) - ss << (chess960 ? to_char(file_of(castling_rook_square(WHITE | KING_SIDE)), false) : 'K'); + ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE | KING_SIDE))) : 'K'); if (can_castle(WHITE_OOO)) - ss << (chess960 ? to_char(file_of(castling_rook_square(WHITE | QUEEN_SIDE)), false) : 'Q'); + ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE | QUEEN_SIDE))) : 'Q'); if (can_castle(BLACK_OO)) - ss << (chess960 ? to_char(file_of(castling_rook_square(BLACK | KING_SIDE)), true) : 'k'); + ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK | KING_SIDE))) : 'k'); if (can_castle(BLACK_OOO)) - ss << (chess960 ? to_char(file_of(castling_rook_square(BLACK | QUEEN_SIDE)), true) : 'q'); + ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK | QUEEN_SIDE))) : 'q'); if (!can_castle(WHITE) && !can_castle(BLACK)) ss << '-'; - ss << (ep_square() == SQ_NONE ? " - " : " " + to_string(ep_square()) + " ") + ss << (ep_square() == SQ_NONE ? " - " : " " + UCI::square(ep_square()) + " ") << st->rule50 << " " << 1 + (gamePly - (sideToMove == BLACK)) / 2; return ss.str(); } -/// Position::pretty() returns an ASCII representation of the position - -const string Position::pretty() const { - - std::ostringstream ss; - - ss << "\n +---+---+---+---+---+---+---+---+\n"; - - for (Rank r = RANK_8; r >= RANK_1; --r) - { - for (File f = FILE_A; f <= FILE_H; ++f) - ss << " | " << PieceToChar[piece_on(make_square(f, r))]; - - ss << " |\n +---+---+---+---+---+---+---+---+\n"; - } - - ss << "\nFen: " << fen() << "\nKey: " << std::hex << std::uppercase - << std::setfill('0') << std::setw(16) << st->key << "\nCheckers: "; - - for (Bitboard b = checkers(); b; ) - ss << to_string(pop_lsb(&b)) << " "; - - 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]; + Value npm = st->nonPawnMaterial[WHITE] + st->nonPawnMaterial[BLACK]; npm = std::max(EndgameLimit, std::min(npm, MidgameLimit)); - return Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit)); + return Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit)); } @@ -497,16 +491,16 @@ Bitboard Position::check_blockers(Color c, Color kingColor) const { /// Position::attackers_to() computes a bitboard of all pieces which attack a -/// given square. Slider attacks use the occ bitboard to indicate occupancy. +/// given square. Slider attacks use the occupied bitboard to indicate occupancy. -Bitboard Position::attackers_to(Square s, Bitboard occ) const { +Bitboard Position::attackers_to(Square s, Bitboard occupied) const { - return (attacks_from(s, BLACK) & pieces(WHITE, PAWN)) - | (attacks_from(s, WHITE) & pieces(BLACK, PAWN)) - | (attacks_from(s) & pieces(KNIGHT)) - | (attacks_bb(s, occ) & pieces(ROOK, QUEEN)) - | (attacks_bb(s, occ) & pieces(BISHOP, QUEEN)) - | (attacks_from(s) & pieces(KING)); + return (attacks_from(s, BLACK) & pieces(WHITE, PAWN)) + | (attacks_from(s, WHITE) & pieces(BLACK, PAWN)) + | (attacks_from(s) & pieces(KNIGHT)) + | (attacks_bb(s, occupied) & pieces(ROOK, QUEEN)) + | (attacks_bb(s, occupied) & pieces(BISHOP, QUEEN)) + | (attacks_from(s) & pieces(KING)); } @@ -531,15 +525,15 @@ bool Position::legal(Move m, Bitboard pinned) const { Square ksq = king_square(us); Square to = to_sq(m); Square capsq = to - pawn_push(us); - Bitboard occ = (pieces() ^ from ^ capsq) | to; + Bitboard occupied = (pieces() ^ from ^ capsq) | to; assert(to == ep_square()); assert(moved_piece(m) == make_piece(us, PAWN)); assert(piece_on(capsq) == make_piece(~us, PAWN)); assert(piece_on(to) == NO_PIECE); - return !(attacks_bb< ROOK>(ksq, occ) & pieces(~us, QUEEN, ROOK)) - && !(attacks_bb(ksq, occ) & pieces(~us, QUEEN, BISHOP)); + return !(attacks_bb< ROOK>(ksq, occupied) & pieces(~us, QUEEN, ROOK)) + && !(attacks_bb(ksq, occupied) & pieces(~us, QUEEN, BISHOP)); } // If the moving piece is a king, check whether the destination @@ -647,7 +641,7 @@ bool Position::gives_check(Move m, const CheckInfo& ci) const { return true; // Is there a discovered check? - if ( unlikely(ci.dcCandidates) + if ( ci.dcCandidates && (ci.dcCandidates & from) && !aligned(from, to, ci.ksq)) return true; @@ -772,7 +766,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI st->pawnKey ^= Zobrist::psq[them][PAWN][capsq]; } else - st->npMaterial[them] -= PieceValue[MG][captured]; + st->nonPawnMaterial[them] -= PieceValue[MG][captured]; // Update board and piece lists remove_piece(capsq, them, captured); @@ -842,7 +836,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI st->psq += psq[us][promotion][to] - psq[us][PAWN][to]; // Update material - st->npMaterial[us] += PieceValue[MG][promotion]; + st->nonPawnMaterial[us] += PieceValue[MG][promotion]; } // Update pawn hash key and prefetch access to pawnsTable @@ -876,7 +870,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI st->checkersBB |= to; // Discovered checks - if (unlikely(ci.dcCandidates) && (ci.dcCandidates & from)) + if (ci.dcCandidates && (ci.dcCandidates & from)) { if (pt != ROOK) st->checkersBB |= attacks_from(king_square(them)) & pieces(us, QUEEN, ROOK); @@ -1013,7 +1007,7 @@ void Position::undo_null_move() { } -/// Position::key_after() computes the new hash key after the given moven. Needed +/// Position::key_after() computes the new hash key after the given move. Needed /// for speculative prefetch. It doesn't recognize special moves like castling, /// en-passant and promotions. @@ -1239,8 +1233,8 @@ bool Position::pos_is_ok(int* step) const { if ( st->key != si.key || st->pawnKey != si.pawnKey || st->materialKey != si.materialKey - || st->npMaterial[WHITE] != si.npMaterial[WHITE] - || st->npMaterial[BLACK] != si.npMaterial[BLACK] + || st->nonPawnMaterial[WHITE] != si.nonPawnMaterial[WHITE] + || st->nonPawnMaterial[BLACK] != si.nonPawnMaterial[BLACK] || st->psq != si.psq || st->checkersBB != si.checkersBB) return false; diff --git a/DroidFish/jni/stockfish/position.h b/DroidFish/jni/stockfish/position.h index 7f7d2c5..447872a 100644 --- a/DroidFish/jni/stockfish/position.h +++ b/DroidFish/jni/stockfish/position.h @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -21,17 +21,18 @@ #define POSITION_H_INCLUDED #include -#include +#include // For offsetof() +#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; struct Thread; +/// CheckInfo struct is initialized at c'tor time and keeps info used to detect +/// if a move gives check. + struct CheckInfo { explicit CheckInfo(const Position&); @@ -39,51 +40,61 @@ struct CheckInfo { Bitboard dcCandidates; Bitboard pinned; Bitboard checkSq[PIECE_TYPE_NB]; - Square ksq; + Square ksq; }; -/// The StateInfo struct stores information needed to restore a Position -/// object to its previous state when we retract a move. Whenever a move -/// is made on the board (by calling Position::do_move), a StateInfo -/// object must be passed as a parameter. +/// StateInfo struct stores information needed to restore a Position object to +/// its previous state when we retract a move. Whenever a move is made on the +/// board (by calling Position::do_move), a StateInfo object must be passed. struct StateInfo { - Key pawnKey, materialKey; - Value npMaterial[COLOR_NB]; - int castlingRights, rule50, pliesFromNull; - Score psq; + + // Copied when making a move + Key pawnKey; + Key materialKey; + Value nonPawnMaterial[COLOR_NB]; + int castlingRights; + int rule50; + int pliesFromNull; + Score psq; Square epSquare; - Key key; - Bitboard checkersBB; - PieceType capturedType; + // Not copied when making a move + Key key; + Bitboard checkersBB; + PieceType capturedType; StateInfo* previous; }; /// When making a move the current StateInfo up to 'key' excluded is copied to -/// the new one. Here we calculate the quad words (64bits) needed to be copied. +/// the new one. Here we calculate the quad words (64 bit) needed to be copied. const size_t StateCopySize64 = offsetof(StateInfo, key) / sizeof(uint64_t) + 1; -/// The Position class stores the information regarding the board representation -/// like pieces, side to move, hash keys, castling info, etc. The most important -/// methods are do_move() and undo_move(), used by the search to update node info -/// when traversing the search tree. +/// Position class stores information regarding the board representation as +/// pieces, side to move, hash keys, castling info, etc. Important methods are +/// do_move() and undo_move(), used by the search to update node info when +/// traversing the search tree. class Position { + + friend std::ostream& operator<<(std::ostream&, const Position&); + + Position(const Position&); // Disable the default copy constructor + public: - Position() {} - Position(const Position& pos, Thread* t) { *this = pos; thisThread = t; } - Position(const std::string& f, bool c960, Thread* t) { set(f, c960, t); } - Position& operator=(const Position&); static void init(); - // Text input/output + Position() {} // To define the global object RootPos + Position(const Position& pos, Thread* th) { *this = pos; thisThread = th; } + Position(const std::string& f, bool c960, Thread* th) { set(f, c960, th); } + Position& operator=(const Position&); // To assign RootPos from UCI + + // FEN string input/output void set(const std::string& fenStr, bool isChess960, Thread* th); const std::string fen() const; - const std::string pretty() const; // Position representation Bitboard pieces() const; @@ -98,7 +109,6 @@ 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; @@ -113,7 +123,7 @@ public: // Attacks to/from a given square Bitboard attackers_to(Square s) const; - Bitboard attackers_to(Square s, Bitboard occ) const; + Bitboard attackers_to(Square s, Bitboard occupied) const; Bitboard attacks_from(Piece pc, Square s) const; template Bitboard attacks_from(Square s) const; template Bitboard attacks_from(Square s, Color c) const; @@ -131,7 +141,6 @@ public: // Piece specific bool pawn_passed(Color c, Square s) const; bool pawn_on_7th(Color c) const; - bool bishop_pair(Color c) const; bool opposite_bishops() const; // Doing and undoing moves @@ -149,12 +158,8 @@ public: Key key() const; Key key_after(Move m) const; Key exclusion_key() const; - Key pawn_key() const; Key material_key() const; - - // Incremental piece-square evaluation - Score psq_score() const; - Value non_pawn_material(Color c) const; + Key pawn_key() const; // Other properties of the position Color side_to_move() const; @@ -166,6 +171,8 @@ public: void set_nodes_searched(uint64_t n); bool is_draw() const; int rule50_count() const; + Score psq_score() const; + Value non_pawn_material(Color c) const; // Position consistency check, for debugging bool pos_is_ok(int* step = NULL) const; @@ -177,7 +184,7 @@ private: void set_castling_right(Color c, Square rfrom); void set_state(StateInfo* si) const; - // Helper functions + // Other helpers Bitboard check_blockers(Color c, Color kingColor) const; void put_piece(Square s, Color c, PieceType pt); void remove_piece(Square s, Color c, PieceType pt); @@ -185,15 +192,13 @@ private: template void do_castling(Square from, Square& to, Square& rfrom, Square& rto); - // Board and pieces + // Data members Piece board[SQUARE_NB]; Bitboard byTypeBB[PIECE_TYPE_NB]; Bitboard byColorBB[COLOR_NB]; int pieceCount[COLOR_NB][PIECE_TYPE_NB]; Square pieceList[COLOR_NB][PIECE_TYPE_NB][16]; int index[SQUARE_NB]; - - // Other info int castlingRightsMask[SQUARE_NB]; Square castlingRookSquare[CASTLING_RIGHT_NB]; Bitboard castlingPath[CASTLING_RIGHT_NB]; @@ -206,12 +211,12 @@ private: bool chess960; }; -inline uint64_t Position::nodes_searched() const { - return nodes; +inline Color Position::side_to_move() const { + return sideToMove; } -inline void Position::set_nodes_searched(uint64_t n) { - nodes = n; +inline bool Position::empty(Square s) const { + return board[s] == NO_PIECE; } inline Piece Position::piece_on(Square s) const { @@ -222,14 +227,6 @@ inline Piece Position::moved_piece(Move m) const { return board[from_sq(m)]; } -inline bool Position::empty(Square s) const { - return board[s] == NO_PIECE; -} - -inline Color Position::side_to_move() const { - return sideToMove; -} - inline Bitboard Position::pieces() const { return byTypeBB[ALL_PIECES]; } @@ -262,14 +259,14 @@ template inline const Square* Position::list(Color c) const { return pieceList[c][Pt]; } -inline Square Position::ep_square() const { - return st->epSquare; -} - inline Square Position::king_square(Color c) const { return pieceList[c][KING][0]; } +inline Square Position::ep_square() const { + return st->epSquare; +} + inline int Position::can_castle(CastlingRight cr) const { return st->castlingRights & cr; } @@ -288,7 +285,6 @@ inline Square Position::castling_rook_square(CastlingRight cr) const { template inline Bitboard Position::attacks_from(Square s) const { - return Pt == BISHOP || Pt == ROOK ? attacks_bb(s, byTypeBB[ALL_PIECES]) : Pt == QUEEN ? attacks_from(s) | attacks_from(s) : StepAttacksBB[Pt][s]; @@ -345,7 +341,7 @@ inline Score Position::psq_score() const { } inline Value Position::non_pawn_material(Color c) const { - return st->npMaterial[c]; + return st->nonPawnMaterial[c]; } inline int Position::game_ply() const { @@ -356,23 +352,20 @@ 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 uint64_t Position::nodes_searched() const { + return nodes; +} + +inline void Position::set_nodes_searched(uint64_t n) { + nodes = n; } inline bool Position::opposite_bishops() const { - return pieceCount[WHITE][BISHOP] == 1 && pieceCount[BLACK][BISHOP] == 1 && opposite_colors(pieceList[WHITE][BISHOP][0], pieceList[BLACK][BISHOP][0]); } -inline bool Position::bishop_pair(Color c) const { - - return pieceCount[c][BISHOP] >= 2 - && opposite_colors(pieceList[c][BISHOP][0], pieceList[c][BISHOP][1]); -} - inline bool Position::pawn_on_7th(Color c) const { return pieces(c, PAWN) & rank_bb(relative_rank(c, RANK_7)); } @@ -389,7 +382,7 @@ inline bool Position::capture_or_promotion(Move m) const { inline bool Position::capture(Move m) const { - // Note that castling is encoded as "king captures the rook" + // Castling is encoded as "king captures the rook" assert(is_ok(m)); return (!empty(to_sq(m)) && type_of(m) != CASTLING) || type_of(m) == ENPASSANT; } @@ -410,14 +403,13 @@ 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]++; + pieceCount[c][ALL_PIECES]++; } inline void Position::move_piece(Square from, Square to, Color c, PieceType pt) { - // index[from] is not updated and becomes stale. This works as long - // as index[] is accessed just by known occupied squares. + // index[from] is not updated and becomes stale. This works as long as index[] + // is accessed just by known occupied squares. Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to]; byTypeBB[ALL_PIECES] ^= from_to_bb; byTypeBB[pt] ^= from_to_bb; @@ -437,13 +429,12 @@ inline void Position::remove_piece(Square s, Color c, PieceType pt) { byTypeBB[ALL_PIECES] ^= s; byTypeBB[pt] ^= s; byColorBB[c] ^= s; - /* board[s] = NO_PIECE; */ // Not needed, will be overwritten by capturing + /* board[s] = NO_PIECE; Not needed, overwritten by the capturing one */ Square lastSquare = pieceList[c][pt][--pieceCount[c][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]--; + pieceCount[c][ALL_PIECES]--; } #endif // #ifndef POSITION_H_INCLUDED diff --git a/DroidFish/jni/stockfish/psqtab.h b/DroidFish/jni/stockfish/psqtab.h index a88d31d..351ae19 100644 --- a/DroidFish/jni/stockfish/psqtab.h +++ b/DroidFish/jni/stockfish/psqtab.h @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 diff --git a/DroidFish/jni/stockfish/rkiss.h b/DroidFish/jni/stockfish/rkiss.h deleted file mode 100644 index f3468db..0000000 --- a/DroidFish/jni/stockfish/rkiss.h +++ /dev/null @@ -1,81 +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 . - - This file is based on original code by Heinz van Saanen and is - available under 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. -*/ - -#ifndef RKISS_H_INCLUDED -#define RKISS_H_INCLUDED - -#include "types.h" - -/// RKISS is our pseudo random number generator (PRNG) used to compute hash keys. -/// George Marsaglia invented the RNG-Kiss-family in the early 90's. This is a -/// specific version that Heinz van Saanen derived from some public domain code -/// by Bob Jenkins. Following the feature list, as tested by Heinz. -/// -/// - Quite platform independent -/// - Passes ALL dieharder tests! Here *nix sys-rand() e.g. fails miserably:-) -/// - ~12 times faster than my *nix sys-rand() -/// - ~4 times faster than SSE2-version of Mersenne twister -/// - Average cycle length: ~2^126 -/// - 64 bit seed -/// - Return doubles with a full 53 bit mantissa -/// - Thread safe - -class RKISS { - - uint64_t a, b, c, d; - - uint64_t rotate_L(uint64_t x, unsigned k) const { - return (x << k) | (x >> (64 - k)); - } - - uint64_t rand64() { - - const uint64_t e = a - rotate_L(b, 7); - a = b ^ rotate_L(c, 13); - b = c + rotate_L(d, 37); - c = d + e; - return d = e + a; - } - -public: - RKISS(int seed = 73) { - - a = 0xF1EA5EED, b = c = d = 0xD4E12C77; - - for (int i = 0; i < seed; ++i) // Scramble a few rounds - rand64(); - } - - template T rand() { return T(rand64()); } - - /// Special generator used to fast init magic numbers. Here the - /// trick is to rotate the randoms of a given quantity 's' known - /// to be optimal to quickly find a good magic candidate. - template T magic_rand(int s) { - return rotate_L(rotate_L(rand(), (s >> 0) & 0x3F) & rand() - , (s >> 6) & 0x3F) & rand(); - } -}; - -#endif // #ifndef RKISS_H_INCLUDED diff --git a/DroidFish/jni/stockfish/search.cpp b/DroidFish/jni/stockfish/search.cpp index 64233d2..0ce836f 100644 --- a/DroidFish/jni/stockfish/search.cpp +++ b/DroidFish/jni/stockfish/search.cpp @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -20,38 +20,43 @@ #include #include #include -#include +#include // For std::memset #include #include #include "evaluate.h" +#include "misc.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" -#include "ucioption.h" +#include "uci.h" +#include "syzygy/tbprobe.h" namespace Search { volatile SignalsType Signals; LimitsType Limits; - std::vector RootMoves; + RootMoveVector RootMoves; Position RootPos; Time::point SearchTime; StateStackPtr SetupStates; - int TBCardinality; - uint64_t TBHits; - bool RootInTB; - bool TB50MoveRule; - Depth TBProbeDepth; - Value TBScore; } +namespace Tablebases { + + int Cardinality; + uint64_t Hits; + bool RootInTB; + bool UseRule50; + Depth ProbeDepth; + Value Score; +} + +namespace TB = Tablebases; + using std::string; using Eval::evaluate; using namespace Search; @@ -65,7 +70,7 @@ namespace { 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] + int FutilityMoveCounts[2][16]; // [improving][depth] inline Value futility_margin(Depth d) { return Value(200 * d); @@ -95,8 +100,9 @@ namespace { void id_loop(Position& pos); Value value_to_tt(Value v, int ply); Value value_from_tt(Value v, int ply); + void update_pv(Move* pv, Move move, Move* childPv); void update_stats(const Position& pos, Stack* ss, Move move, Depth depth, Move* quiets, int quietsCnt); - string uci_pv(const Position& pos, int depth, Value alpha, Value beta); + string uci_pv(const Position& pos, Depth depth, Value alpha, Value beta); struct Skill { Skill(int l, size_t rootSize) : level(l), @@ -109,7 +115,7 @@ namespace { } size_t candidates_size() const { return candidates; } - bool time_to_pick(int depth) const { return depth == 1 + level; } + bool time_to_pick(Depth depth) const { return depth / ONE_PLY == 1 + level; } Move pick_move(); int level; @@ -124,28 +130,26 @@ namespace { void Search::init() { - int d; // depth (ONE_PLY == 2) - int hd; // half depth (ONE_PLY == 1) - int mc; // moveCount - // Init reductions array - for (hd = 1; hd < 64; ++hd) for (mc = 1; mc < 64; ++mc) - { - double pvRed = 0.00 + log(double(hd)) * log(double(mc)) / 3.00; - double nonPVRed = 0.33 + log(double(hd)) * log(double(mc)) / 2.25; + for (int d = 1; d < 64; ++d) + for (int mc = 1; mc < 64; ++mc) + { + double pvRed = 0.00 + log(double(d)) * log(double(mc)) / 3.00; + double nonPVRed = 0.33 + log(double(d)) * log(double(mc)) / 2.25; - 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][1][d][mc] = int8_t( pvRed >= 1.0 ? pvRed + 0.5: 0); + Reductions[0][1][d][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]; + Reductions[1][0][d][mc] = Reductions[1][1][d][mc]; + Reductions[0][0][d][mc] = Reductions[0][1][d][mc]; - if (Reductions[0][0][hd][mc] >= 2) - Reductions[0][0][hd][mc] += 1; - } + // Increase reduction when eval is not improving + if (Reductions[0][0][d][mc] >= 2) + Reductions[0][0][d][mc] += 1; + } // Init futility move count array - for (d = 0; d < 32; ++d) + for (int d = 0; d < 16; ++d) { 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)); @@ -175,7 +179,7 @@ uint64_t Search::perft(Position& pos, Depth depth) { pos.undo_move(*it); } if (Root) - sync_cout << move_to_uci(*it, pos.is_chess960()) << ": " << cnt << sync_endl; + sync_cout << UCI::move(*it, pos.is_chess960()) << ": " << cnt << sync_endl; } return nodes; } @@ -189,98 +193,75 @@ template uint64_t Search::perft(Position& pos, Depth depth); void Search::think() { - TimeMgr.init(Limits, RootPos.game_ply(), RootPos.side_to_move()); - int piecesCnt; - TBHits = TBCardinality = 0; - RootInTB = false; + TimeMgr.init(Limits, RootPos.side_to_move(), RootPos.game_ply()); - 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); + int contempt = Options["Contempt"] * PawnValueEg / 100; // From centipawns + DrawValue[ RootPos.side_to_move()] = VALUE_DRAW - Value(contempt); + DrawValue[~RootPos.side_to_move()] = VALUE_DRAW + Value(contempt); + + TB::Hits = 0; + TB::RootInTB = false; + TB::UseRule50 = Options["Syzygy50MoveRule"]; + TB::ProbeDepth = Options["SyzygyProbeDepth"] * ONE_PLY; + TB::Cardinality = Options["SyzygyProbeLimit"]; + + // Skip TB probing when no TB found: !TBLargest -> !TB::Cardinality + if (TB::Cardinality > TB::MaxCardinality) + { + TB::Cardinality = TB::MaxCardinality; + TB::ProbeDepth = DEPTH_ZERO; + } if (RootMoves.empty()) { RootMoves.push_back(MOVE_NONE); sync_cout << "info depth 0 score " - << score_to_uci(RootPos.checkers() ? -VALUE_MATE : VALUE_DRAW) + << UCI::value(RootPos.checkers() ? -VALUE_MATE : VALUE_DRAW) << sync_endl; - - goto finalize; } - - piecesCnt = RootPos.total_piece_count(); - TBCardinality = Options["SyzygyProbeLimit"]; - TBProbeDepth = Options["SyzygyProbeDepth"] * ONE_PLY; - if (TBCardinality > Tablebases::TBLargest) + else { - TBCardinality = Tablebases::TBLargest; - TBProbeDepth = 0 * ONE_PLY; + if (TB::Cardinality >= RootPos.count(WHITE) + + RootPos.count(BLACK)) + { + // If the current root position is in the tablebases then RootMoves + // contains only moves that preserve the draw or win. + TB::RootInTB = Tablebases::root_probe(RootPos, RootMoves, TB::Score); + + if (TB::RootInTB) + TB::Cardinality = 0; // Do not probe tablebases during the search + + else // If DTZ tables are missing, use WDL tables as a fallback + { + // Filter out moves that do not preserve a draw or win + TB::RootInTB = Tablebases::root_probe_wdl(RootPos, RootMoves, TB::Score); + + // Only probe during search if winning + if (TB::Score <= VALUE_DRAW) + TB::Cardinality = 0; + } + + if (TB::RootInTB) + { + TB::Hits = RootMoves.size(); + + if (!TB::UseRule50) + TB::Score = TB::Score > VALUE_DRAW ? VALUE_MATE - MAX_PLY - 1 + : TB::Score < VALUE_DRAW ? -VALUE_MATE + MAX_PLY + 1 + : VALUE_DRAW; + } + } + + for (size_t i = 0; i < Threads.size(); ++i) + Threads[i]->maxPly = 0; + + Threads.timer->run = true; + Threads.timer->notify_one(); // Wake up the recurring timer + + id_loop(RootPos); // Let's start searching ! + + Threads.timer->run = false; } - TB50MoveRule = Options["Syzygy50MoveRule"]; - - if (piecesCnt <= TBCardinality) - { - TBHits = RootMoves.size(); - - // If the current root position is in the tablebases then RootMoves - // contains only moves that preserve the draw or win. - RootInTB = Tablebases::root_probe(RootPos, TBScore); - - if (RootInTB) - { - TBCardinality = 0; // Do not probe tablebases during the search - - // It might be a good idea to mangle the hash key (xor it - // with a fixed value) in order to "clear" the hash table of - // the results of previous probes. However, that would have to - // be done from within the Position class, so we skip it for now. - - // Optional: decrease target time. - } - else // If DTZ tables are missing, use WDL tables as a fallback - { - // Filter out moves that do not preserve a draw or win - RootInTB = Tablebases::root_probe_wdl(RootPos, TBScore); - - // Only probe during search if winning - if (TBScore <= VALUE_DRAW) - TBCardinality = 0; - } - - if (!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 - for (size_t i = 0; i < Threads.size(); ++i) - Threads[i]->maxPly = 0; - - Threads.timer->run = true; - Threads.timer->notify_one(); // Wake up the recurring timer - - id_loop(RootPos); // Let's start searching ! - - Threads.timer->run = false; // Stop the timer - - if (RootInTB) - { - // 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 // Signals.stop. However, if we are pondering or in an infinite search, @@ -293,10 +274,12 @@ finalize: RootPos.this_thread()->wait_for(Signals.stop); } - // Best move could be MOVE_NONE when searching on a stalemate position - sync_cout << "bestmove " << move_to_uci(RootMoves[0].pv[0], RootPos.is_chess960()) - << " ponder " << move_to_uci(RootMoves[0].pv[1], RootPos.is_chess960()) - << sync_endl; + sync_cout << "bestmove " << UCI::move(RootMoves[0].pv[0], RootPos.is_chess960()); + + if (RootMoves[0].pv.size() > 1 || RootMoves[0].extract_ponder_from_tt(RootPos)) + std::cout << " ponder " << UCI::move(RootMoves[0].pv[1], RootPos.is_chess960()); + + std::cout << sync_endl; } @@ -308,13 +291,13 @@ namespace { void id_loop(Position& pos) { - Stack stack[MAX_PLY_PLUS_6], *ss = stack+2; // To allow referencing (ss-2) - int depth; + Stack stack[MAX_PLY+4], *ss = stack+2; // To allow referencing (ss-2) and (ss+2) + Depth depth; Value bestValue, alpha, beta, delta; std::memset(ss-2, 0, 5 * sizeof(Stack)); - depth = 0; + depth = DEPTH_ZERO; BestMoveChanges = 0; bestValue = delta = alpha = -VALUE_INFINITE; beta = VALUE_INFINITE; @@ -333,7 +316,7 @@ namespace { 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)) + while (++depth < DEPTH_MAX && !Signals.stop && (!Limits.depth || depth <= Limits.depth)) { // Age out PV variability metric BestMoveChanges *= 0.5; @@ -341,17 +324,17 @@ namespace { // Save the last iteration's scores before first PV line is searched and // all the move scores except the (new) PV are set to -VALUE_INFINITE. for (size_t i = 0; i < RootMoves.size(); ++i) - RootMoves[i].prevScore = RootMoves[i].score; + RootMoves[i].previousScore = RootMoves[i].score; // MultiPV loop. We perform a full root search for each PV line for (PVIdx = 0; PVIdx < std::min(multiPV, RootMoves.size()) && !Signals.stop; ++PVIdx) { // Reset aspiration window starting size - if (depth >= 5) + if (depth >= 5 * ONE_PLY) { delta = Value(16); - alpha = std::max(RootMoves[PVIdx].prevScore - delta,-VALUE_INFINITE); - beta = std::min(RootMoves[PVIdx].prevScore + delta, VALUE_INFINITE); + alpha = std::max(RootMoves[PVIdx].previousScore - delta,-VALUE_INFINITE); + beta = std::min(RootMoves[PVIdx].previousScore + delta, VALUE_INFINITE); } // Start with a small aspiration window and, in the case of a fail @@ -359,7 +342,7 @@ namespace { // high/low anymore. while (true) { - bestValue = search(pos, ss, alpha, beta, depth * ONE_PLY, false); + bestValue = search(pos, ss, alpha, beta, depth, false); // Bring the best move to the front. It is critical that sorting // is done with a stable algorithm because all the values but the @@ -382,7 +365,8 @@ namespace { // When failing high/low give some update (without cluttering // the UI) before a re-search. - if ( (bestValue <= alpha || bestValue >= beta) + if ( multiPV == 1 + && (bestValue <= alpha || bestValue >= beta) && Time::now() - SearchTime > 3000) sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl; @@ -390,18 +374,21 @@ namespace { // re-search, otherwise exit the loop. if (bestValue <= alpha) { + beta = (alpha + beta) / 2; alpha = std::max(bestValue - delta, -VALUE_INFINITE); Signals.failedLowAtRoot = true; Signals.stopOnPonderhit = false; } else if (bestValue >= beta) + { + alpha = (alpha + beta) / 2; beta = std::min(bestValue + delta, VALUE_INFINITE); - + } else break; - delta += 3 * delta / 8; + delta += delta / 2; assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE); } @@ -409,7 +396,12 @@ namespace { // Sort the PV lines searched so far and update the GUI std::stable_sort(RootMoves.begin(), RootMoves.begin() + PVIdx + 1); - if (PVIdx + 1 == std::min(multiPV, RootMoves.size()) || Time::now() - SearchTime > 3000) + if (Signals.stop) + sync_cout << "info nodes " << RootPos.nodes_searched() + << " time " << Time::now() - SearchTime << sync_endl; + + else if ( PVIdx + 1 == std::min(multiPV, RootMoves.size()) + || Time::now() - SearchTime > 3000) sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl; } @@ -427,7 +419,7 @@ namespace { if (Limits.use_time_management() && !Signals.stop && !Signals.stopOnPonderhit) { // Take some extra time if the best move has changed - if (depth > 4 && multiPV == 1) + if (depth > 4 * ONE_PLY && multiPV == 1) TimeMgr.pv_instability(BestMoveChanges); // Stop the search if only one legal move is available or all @@ -464,15 +456,15 @@ namespace { assert(PvNode || (alpha == beta - 1)); assert(depth > DEPTH_ZERO); - Move quietsSearched[64]; + Move pv[MAX_PLY+1], quietsSearched[64]; StateInfo st; - const TTEntry *tte; + TTEntry* tte; SplitPoint* splitPoint; Key posKey; Move ttMove, move, excludedMove, bestMove; - Depth ext, newDepth, predictedDepth; + Depth extension, newDepth, predictedDepth; Value bestValue, value, ttValue, eval, nullValue, futilityValue; - bool inCheck, givesCheck, pvMove, singularExtensionNode, improving; + bool ttHit, inCheck, givesCheck, singularExtensionNode, improving; bool captureOrPromotion, dangerous, doFullDepthSearch; int moveCount, quietCount; @@ -486,6 +478,7 @@ namespace { bestMove = splitPoint->bestMove; bestValue = splitPoint->bestValue; tte = NULL; + ttHit = false; ttMove = excludedMove = MOVE_NONE; ttValue = VALUE_NONE; @@ -496,10 +489,7 @@ namespace { moveCount = quietCount = 0; bestValue = -VALUE_INFINITE; - ss->currentMove = ss->ttMove = (ss+1)->excludedMove = bestMove = MOVE_NONE; ss->ply = (ss-1)->ply + 1; - (ss+1)->skipNullMove = false; (ss+1)->reduction = DEPTH_ZERO; - (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; // Used to send selDepth info to GUI if (PvNode && thisThread->maxPly < ss->ply) @@ -508,8 +498,8 @@ namespace { if (!RootNode) { // Step 2. Check for aborted search and immediate draw - if (Signals.stop || pos.is_draw() || ss->ply > MAX_PLY) - return ss->ply > MAX_PLY && !inCheck ? evaluate(pos) : DrawValue[pos.side_to_move()]; + if (Signals.stop || pos.is_draw() || ss->ply >= MAX_PLY) + return ss->ply >= MAX_PLY && !inCheck ? evaluate(pos) : DrawValue[pos.side_to_move()]; // Step 3. Mate distance pruning. Even if we mate at the next move our score // would be at best mate_in(ss->ply+1), but if alpha is already bigger because @@ -523,26 +513,28 @@ namespace { return alpha; } + assert(0 <= ss->ply && ss->ply < MAX_PLY); + + ss->currentMove = ss->ttMove = (ss+1)->excludedMove = bestMove = MOVE_NONE; + (ss+1)->skipEarlyPruning = false; (ss+1)->reduction = DEPTH_ZERO; + (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; + // Step 4. Transposition table lookup // We don't want the score of a partial search to overwrite a previous full search // TT value, so we use a different position key in case of an excluded move. excludedMove = ss->excludedMove; posKey = excludedMove ? pos.exclusion_key() : pos.key(); - tte = TT.probe(posKey); - ss->ttMove = ttMove = RootNode ? RootMoves[PVIdx].pv[0] : tte ? tte->move() : MOVE_NONE; - ttValue = tte ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE; + tte = TT.probe(posKey, ttHit); + ss->ttMove = ttMove = RootNode ? RootMoves[PVIdx].pv[0] : ttHit ? tte->move() : MOVE_NONE; + ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE; - // At PV nodes we check for exact scores, whilst at non-PV nodes we check for - // a fail high/low. The biggest advantage to probing at PV nodes is to have a - // smooth experience in analysis mode. We don't probe at Root nodes otherwise - // we should also update RootMoveList to avoid bogus output. - if ( !RootNode - && tte + // At non-PV nodes we check for a fail high/low. We don't probe at PV nodes + if ( !PvNode + && ttHit && tte->depth() >= depth && ttValue != VALUE_NONE // Only in case of TT access race - && ( PvNode ? tte->bound() == BOUND_EXACT - : ttValue >= beta ? (tte->bound() & BOUND_LOWER) - : (tte->bound() & BOUND_UPPER))) + && (ttValue >= beta ? (tte->bound() & BOUND_LOWER) + : (tte->bound() & BOUND_UPPER))) { ss->currentMove = ttMove; // Can be MOVE_NONE @@ -554,33 +546,32 @@ namespace { } // Step 4a. Tablebase probe - if ( !RootNode - && pos.total_piece_count() <= TBCardinality - && ( pos.total_piece_count() < TBCardinality || depth >= TBProbeDepth ) - && pos.rule50_count() == 0) + if (!RootNode && TB::Cardinality) { - int found, v = Tablebases::probe_wdl(pos, &found); + int piecesCnt = pos.count(WHITE) + pos.count(BLACK); - if (found) + if ( piecesCnt <= TB::Cardinality + && (piecesCnt < TB::Cardinality || depth >= TB::ProbeDepth) + && pos.rule50_count() == 0) { - TBHits++; + int found, v = Tablebases::probe_wdl(pos, &found); - if (TB50MoveRule) { - value = v < -1 ? -VALUE_MATE + MAX_PLY + ss->ply - : v > 1 ? VALUE_MATE - MAX_PLY - ss->ply - : VALUE_DRAW + 2 * v; - } - else + if (found) { - value = v < 0 ? -VALUE_MATE + MAX_PLY + ss->ply - : v > 0 ? VALUE_MATE - MAX_PLY - ss->ply - : VALUE_DRAW; + TB::Hits++; + + int drawScore = TB::UseRule50 ? 1 : 0; + + value = v < -drawScore ? -VALUE_MATE + MAX_PLY + ss->ply + : v > drawScore ? VALUE_MATE - MAX_PLY - ss->ply + : VALUE_DRAW + 2 * v * drawScore; + + tte->save(posKey, value_to_tt(value, ss->ply), BOUND_EXACT, + std::min(DEPTH_MAX - ONE_PLY, depth + 6 * ONE_PLY), + MOVE_NONE, VALUE_NONE, TT.generation()); + + return value; } - - TT.store(posKey, value_to_tt(value, ss->ply), BOUND_EXACT, - depth + 6 * ONE_PLY, MOVE_NONE, VALUE_NONE); - - return value; } } @@ -591,10 +582,10 @@ namespace { goto moves_loop; } - else if (tte) + else if (ttHit) { // Never assume anything on values stored in TT - if ((ss->staticEval = eval = tte->eval_value()) == VALUE_NONE) + if ((ss->staticEval = eval = tte->eval()) == VALUE_NONE) eval = ss->staticEval = evaluate(pos); // Can ttValue be used as a better position evaluation? @@ -607,9 +598,12 @@ namespace { 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); + tte->save(posKey, VALUE_NONE, BOUND_NONE, DEPTH_NONE, MOVE_NONE, ss->staticEval, TT.generation()); } + if (ss->skipEarlyPruning) + goto moves_loop; + if ( !pos.captured_piece_type() && ss->staticEval != VALUE_NONE && (ss-1)->staticEval != VALUE_NONE @@ -639,8 +633,7 @@ namespace { } // Step 7. Futility pruning: child node (skipped when in check) - if ( !PvNode - && !ss->skipNullMove + if ( !RootNode && depth < 7 * ONE_PLY && eval - futility_margin(depth) >= beta && eval < VALUE_KNOWN_WIN // Do not return unproven wins @@ -649,7 +642,6 @@ namespace { // Step 8. Null move search with verification search (is omitted in PV nodes) if ( !PvNode - && !ss->skipNullMove && depth >= 2 * ONE_PLY && eval >= beta && pos.non_pawn_material(pos.side_to_move())) @@ -659,13 +651,13 @@ namespace { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and value - Depth R = (3 + depth / 4 + std::min(int(eval - beta) / PawnValueMg, 3)) * ONE_PLY; + Depth R = ((823 + 67 * depth) / 256 + std::min((eval - beta) / PawnValueMg, 3)) * ONE_PLY; pos.do_null_move(st); - (ss+1)->skipNullMove = true; + (ss+1)->skipEarlyPruning = true; nullValue = depth-R < ONE_PLY ? -qsearch(pos, ss+1, -beta, -beta+1, DEPTH_ZERO) : - search(pos, ss+1, -beta, -beta+1, depth-R, !cutNode); - (ss+1)->skipNullMove = false; + (ss+1)->skipEarlyPruning = false; pos.undo_null_move(); if (nullValue >= beta) @@ -678,10 +670,10 @@ namespace { return nullValue; // Do verification search at high depths - ss->skipNullMove = true; + ss->skipEarlyPruning = true; Value v = depth-R < ONE_PLY ? qsearch(pos, ss, beta-1, beta, DEPTH_ZERO) : search(pos, ss, beta-1, beta, depth-R, false); - ss->skipNullMove = false; + ss->skipEarlyPruning = false; if (v >= beta) return nullValue; @@ -694,7 +686,6 @@ namespace { // prune the previous move. if ( !PvNode && depth >= 5 * ONE_PLY - && !ss->skipNullMove && abs(beta) < VALUE_MATE_IN_MAX_PLY) { Value rbeta = std::min(beta + 200, VALUE_INFINITE); @@ -725,12 +716,12 @@ namespace { && (PvNode || ss->staticEval + 256 >= beta)) { Depth d = 2 * (depth - 2 * ONE_PLY) - (PvNode ? DEPTH_ZERO : depth / 2); - ss->skipNullMove = true; + ss->skipEarlyPruning = true; search(pos, ss, alpha, beta, d / 2, true); - ss->skipNullMove = false; + ss->skipEarlyPruning = false; - tte = TT.probe(posKey); - ttMove = tte ? tte->move() : MOVE_NONE; + tte = TT.probe(posKey, ttHit); + ttMove = ttHit ? tte->move() : MOVE_NONE; } moves_loop: // When in check and at SpNode search starts from here @@ -792,12 +783,15 @@ 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 - << " currmove " << move_to_uci(move, pos.is_chess960()) + sync_cout << "info depth " << depth / ONE_PLY + << " currmove " << UCI::move(move, pos.is_chess960()) << " currmovenumber " << moveCount + PVIdx << sync_endl; } - ext = DEPTH_ZERO; + if (PvNode) + (ss+1)->pv = NULL; + + extension = DEPTH_ZERO; captureOrPromotion = pos.capture_or_promotion(move); givesCheck = type_of(move) == NORMAL && !ci.dcCandidates @@ -810,7 +804,7 @@ moves_loop: // When in check and at SpNode search starts from here // Step 12. Extend checks if (givesCheck && pos.see_sign(move) >= VALUE_ZERO) - ext = ONE_PLY; + extension = ONE_PLY; // Singular extension search. If all moves but one fail low on a search of // (alpha-s, beta-s), and just one fails high on (alpha, beta), then that move @@ -819,29 +813,28 @@ moves_loop: // When in check and at SpNode search starts from here // ttValue minus a margin then we extend the ttMove. if ( singularExtensionNode && move == ttMove - && !ext + && !extension && pos.legal(move, ci.pinned)) { - Value rBeta = ttValue - int(2 * depth); + Value rBeta = ttValue - 2 * depth / ONE_PLY; ss->excludedMove = move; - ss->skipNullMove = true; + ss->skipEarlyPruning = true; value = search(pos, ss, rBeta - 1, rBeta, depth / 2, cutNode); - ss->skipNullMove = false; + ss->skipEarlyPruning = false; ss->excludedMove = MOVE_NONE; if (value < rBeta) - ext = ONE_PLY; + extension = ONE_PLY; } // Update the current move (this must be done after singular extension search) - newDepth = depth - ONE_PLY + ext; + newDepth = depth - ONE_PLY + extension; - // Step 13. Pruning at shallow depth (exclude PV nodes) - if ( !PvNode + // Step 13. Pruning at shallow depth + if ( !RootNode && !captureOrPromotion && !inCheck && !dangerous - /* && move != ttMove Already implicit in the next condition */ && bestValue > VALUE_MATED_IN_MAX_PLY) { // Move count based pruning @@ -896,7 +889,6 @@ moves_loop: // When in check and at SpNode search starts from here continue; } - pvMove = PvNode && moveCount == 1; ss->currentMove = move; if (!SpNode && !captureOrPromotion && quietCount < 64) quietsSearched[quietCount++] = move; @@ -907,16 +899,15 @@ moves_loop: // When in check and at SpNode search starts from here // Step 15. Reduced depth search (LMR). If the move fails high it will be // re-searched at full depth. if ( depth >= 3 * ONE_PLY - && !pvMove + && moveCount > 1 && !captureOrPromotion - && move != ttMove && move != ss->killers[0] && move != ss->killers[1]) { ss->reduction = reduction(improving, depth, moveCount); if ( (!PvNode && cutNode) - || History[pos.piece_on(to_sq(move))][to_sq(move)] < 0) + || History[pos.piece_on(to_sq(move))][to_sq(move)] < VALUE_ZERO) ss->reduction += ONE_PLY; if (move == countermoves[0] || move == countermoves[1]) @@ -926,7 +917,7 @@ moves_loop: // When in check and at SpNode search starts from here 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) + && pos.see(make_move(to_sq(move), from_sq(move))) < VALUE_ZERO) ss->reduction = std::max(DEPTH_ZERO, ss->reduction - ONE_PLY); Depth d = std::max(newDepth - ss->reduction, ONE_PLY); @@ -946,7 +937,7 @@ moves_loop: // When in check and at SpNode search starts from here ss->reduction = DEPTH_ZERO; } else - doFullDepthSearch = !pvMove; + doFullDepthSearch = !PvNode || moveCount > 1; // Step 16. Full depth search, when LMR is skipped or fails high if (doFullDepthSearch) @@ -963,11 +954,17 @@ moves_loop: // When in check and at SpNode search starts from here // 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)))) + if (PvNode && (moveCount == 1 || (value > alpha && (RootNode || value < beta)))) + { + (ss+1)->pv = pv; + (ss+1)->pv[0] = MOVE_NONE; + 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); @@ -992,15 +989,20 @@ moves_loop: // When in check and at SpNode search starts from here RootMove& rm = *std::find(RootMoves.begin(), RootMoves.end(), move); // PV move or new best move ? - if (pvMove || value > alpha) + if (moveCount == 1 || value > alpha) { rm.score = value; - rm.extract_pv_from_tt(pos); + rm.pv.resize(1); + + assert((ss+1)->pv); + + for (Move* m = (ss+1)->pv; *m != MOVE_NONE; ++m) + rm.pv.push_back(*m); // We record how often the best move has been changed in each // iteration. This information is used for time management: When // the best move changes frequently, we allocate some more time. - if (!pvMove) + if (moveCount > 1) ++BestMoveChanges; } else @@ -1018,6 +1020,9 @@ moves_loop: // When in check and at SpNode search starts from here { bestMove = SpNode ? splitPoint->bestMove = move : move; + if (PvNode && !RootNode) // Update pv even in fail-high case + update_pv(SpNode ? splitPoint->ss->pv : ss->pv, move, (ss+1)->pv); + if (PvNode && value < beta) // Update alpha! Always alpha < beta alpha = SpNode ? splitPoint->alpha = value : value; else @@ -1076,10 +1081,10 @@ moves_loop: // When in check and at SpNode search starts from here else if (bestValue >= beta && !pos.capture_or_promotion(bestMove) && !inCheck) update_stats(pos, ss, bestMove, depth, quietsSearched, quietCount - 1); - TT.store(posKey, value_to_tt(bestValue, ss->ply), - bestValue >= beta ? BOUND_LOWER : - PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER, - depth, bestMove, ss->staticEval); + tte->save(posKey, value_to_tt(bestValue, ss->ply), + bestValue >= beta ? BOUND_LOWER : + PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER, + depth, bestMove, ss->staticEval, TT.generation()); assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); @@ -1102,24 +1107,30 @@ moves_loop: // When in check and at SpNode search starts from here assert(PvNode || (alpha == beta - 1)); assert(depth <= DEPTH_ZERO); + Move pv[MAX_PLY+1]; StateInfo st; - const TTEntry* tte; + TTEntry* tte; Key posKey; Move ttMove, move, bestMove; Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha; - bool givesCheck, evasionPrunable; + bool ttHit, givesCheck, evasionPrunable; Depth ttDepth; - // To flag BOUND_EXACT a node with eval above alpha and no available moves if (PvNode) - oldAlpha = alpha; + { + oldAlpha = alpha; // To flag BOUND_EXACT when eval above alpha and no available moves + (ss+1)->pv = pv; + ss->pv[0] = MOVE_NONE; + } ss->currentMove = bestMove = MOVE_NONE; ss->ply = (ss-1)->ply + 1; // Check for an instant draw or if the maximum ply has been reached - if (pos.is_draw() || ss->ply > MAX_PLY) - return ss->ply > MAX_PLY && !InCheck ? evaluate(pos) : DrawValue[pos.side_to_move()]; + if (pos.is_draw() || ss->ply >= MAX_PLY) + return ss->ply >= MAX_PLY && !InCheck ? evaluate(pos) : DrawValue[pos.side_to_move()]; + + assert(0 <= ss->ply && ss->ply < MAX_PLY); // Decide whether or not to include checks: this fixes also the type of // TT entry depth that we are going to use. Note that in qsearch we use @@ -1129,16 +1140,16 @@ moves_loop: // When in check and at SpNode search starts from here // Transposition table lookup posKey = pos.key(); - tte = TT.probe(posKey); - ttMove = tte ? tte->move() : MOVE_NONE; - ttValue = tte ? value_from_tt(tte->value(),ss->ply) : VALUE_NONE; + tte = TT.probe(posKey, ttHit); + ttMove = ttHit ? tte->move() : MOVE_NONE; + ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE; - if ( tte + if ( !PvNode + && ttHit && tte->depth() >= ttDepth && ttValue != VALUE_NONE // Only in case of TT access race - && ( PvNode ? tte->bound() == BOUND_EXACT - : ttValue >= beta ? (tte->bound() & BOUND_LOWER) - : (tte->bound() & BOUND_UPPER))) + && (ttValue >= beta ? (tte->bound() & BOUND_LOWER) + : (tte->bound() & BOUND_UPPER))) { ss->currentMove = ttMove; // Can be MOVE_NONE return ttValue; @@ -1152,10 +1163,10 @@ moves_loop: // When in check and at SpNode search starts from here } else { - if (tte) + if (ttHit) { // Never assume anything on values stored in TT - if ((ss->staticEval = bestValue = tte->eval_value()) == VALUE_NONE) + if ((ss->staticEval = bestValue = tte->eval()) == VALUE_NONE) ss->staticEval = bestValue = evaluate(pos); // Can ttValue be used as a better position evaluation? @@ -1170,9 +1181,9 @@ moves_loop: // When in check and at SpNode search starts from here // Stand pat. Return immediately if static value is at least beta if (bestValue >= beta) { - if (!tte) - TT.store(pos.key(), value_to_tt(bestValue, ss->ply), BOUND_LOWER, - DEPTH_NONE, MOVE_NONE, ss->staticEval); + if (!ttHit) + tte->save(pos.key(), value_to_tt(bestValue, ss->ply), BOUND_LOWER, + DEPTH_NONE, MOVE_NONE, ss->staticEval, TT.generation()); return bestValue; } @@ -1200,10 +1211,8 @@ moves_loop: // When in check and at SpNode search starts from here : pos.gives_check(move, ci); // Futility pruning - if ( !PvNode - && !InCheck + if ( !InCheck && !givesCheck - && move != ttMove && futilityBase > -VALUE_KNOWN_WIN && !pos.advanced_pawn_push(move)) { @@ -1211,13 +1220,13 @@ moves_loop: // When in check and at SpNode search starts from here futilityValue = futilityBase + PieceValue[EG][pos.piece_on(to_sq(move))]; - if (futilityValue < beta) + if (futilityValue <= alpha) { bestValue = std::max(bestValue, futilityValue); continue; } - if (futilityBase < beta && pos.see(move) <= VALUE_ZERO) + if (futilityBase <= alpha && pos.see(move) <= VALUE_ZERO) { bestValue = std::max(bestValue, futilityBase); continue; @@ -1231,9 +1240,7 @@ moves_loop: // When in check and at SpNode search starts from here && !pos.can_castle(pos.side_to_move()); // Don't search moves with negative SEE values - if ( !PvNode - && (!InCheck || evasionPrunable) - && move != ttMove + if ( (!InCheck || evasionPrunable) && type_of(move) != PROMOTION && pos.see_sign(move) < VALUE_ZERO) continue; @@ -1262,6 +1269,9 @@ moves_loop: // When in check and at SpNode search starts from here if (value > alpha) { + if (PvNode) // Update pv even in fail-high case + update_pv(ss->pv, move, (ss+1)->pv); + if (PvNode && value < beta) // Update alpha here! Always alpha < beta { alpha = value; @@ -1269,8 +1279,8 @@ moves_loop: // When in check and at SpNode search starts from here } else // Fail high { - TT.store(posKey, value_to_tt(value, ss->ply), BOUND_LOWER, - ttDepth, move, ss->staticEval); + tte->save(posKey, value_to_tt(value, ss->ply), BOUND_LOWER, + ttDepth, move, ss->staticEval, TT.generation()); return value; } @@ -1283,9 +1293,9 @@ moves_loop: // When in check and at SpNode search starts from here if (InCheck && bestValue == -VALUE_INFINITE) return mated_in(ss->ply); // Plies to mate from the root - TT.store(posKey, value_to_tt(bestValue, ss->ply), - PvNode && bestValue > oldAlpha ? BOUND_EXACT : BOUND_UPPER, - ttDepth, bestMove, ss->staticEval); + tte->save(posKey, value_to_tt(bestValue, ss->ply), + PvNode && bestValue > oldAlpha ? BOUND_EXACT : BOUND_UPPER, + ttDepth, bestMove, ss->staticEval, TT.generation()); assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); @@ -1318,6 +1328,15 @@ moves_loop: // When in check and at SpNode search starts from here } + // update_pv() adds current move and appends child pv[] + + void update_pv(Move* pv, Move move, Move* childPv) { + + for (*pv++ = move; childPv && *childPv != MOVE_NONE; ) + *pv++ = *childPv++; + *pv = MOVE_NONE; + } + // update_stats() updates killers, history, countermoves and followupmoves stats after a fail-high // of a quiet move. @@ -1331,7 +1350,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(4 * int(depth) * int(depth)); + Value bonus = Value((depth / ONE_PLY) * (depth / ONE_PLY)); History.update(pos.moved_piece(move), to_sq(move), bonus); for (int i = 0; i < quietsCnt; ++i) { @@ -1358,16 +1377,13 @@ moves_loop: // When in check and at SpNode search starts from here Move Skill::pick_move() { - static RKISS rk; - - // PRNG sequence should be not deterministic - for (int i = Time::now() % 50; i > 0; --i) - rk.rand(); + // PRNG sequence should be non-deterministic, so we seed it with the time at init + static PRNG rng(Time::now()); // RootMoves are already sorted by score in descending order int variance = std::min(RootMoves[0].score - RootMoves[candidates - 1].score, PawnValueMg); int weakness = 120 - 2 * level; - int max_s = -VALUE_INFINITE; + int maxScore = -VALUE_INFINITE; best = MOVE_NONE; // Choose best move. For each move score we add two terms both dependent on @@ -1375,19 +1391,19 @@ moves_loop: // When in check and at SpNode search starts from here // then we choose the move with the resulting highest score. for (size_t i = 0; i < candidates; ++i) { - int s = RootMoves[i].score; + int score = 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 > score + 2 * PawnValueMg) break; // This is our magic formula - s += ( weakness * int(RootMoves[0].score - s) - + variance * (rk.rand() % weakness)) / 128; + score += ( weakness * int(RootMoves[0].score - score) + + variance * (rng.rand() % weakness)) / 128; - if (s > max_s) + if (score > maxScore) { - max_s = s; + maxScore = score; best = RootMoves[i].pv[0]; } } @@ -1399,7 +1415,7 @@ moves_loop: // When in check and at SpNode search starts from here // requires that all (if any) unsearched PV lines are sent using a previous // search score. - string uci_pv(const Position& pos, int depth, Value alpha, Value beta) { + string uci_pv(const Position& pos, Depth depth, Value alpha, Value beta) { std::stringstream ss; Time::point elapsed = Time::now() - SearchTime + 1; @@ -1414,36 +1430,34 @@ moves_loop: // When in check and at SpNode search starts from here { bool updated = (i <= PVIdx); - if (depth == 1 && !updated) + if (depth == ONE_PLY && !updated) continue; - int d = updated ? depth : depth - 1; - Value v = updated ? RootMoves[i].score : RootMoves[i].prevScore; + Depth d = updated ? depth : depth - ONE_PLY; + Value v = updated ? RootMoves[i].score : RootMoves[i].previousScore; - bool tb = RootInTB; - if (tb) - { - if (abs(v) >= VALUE_MATE - MAX_PLY) - tb = false; - else - v = TBScore; - } + bool tb = TB::RootInTB && abs(v) < VALUE_MATE - MAX_PLY; + v = tb ? TB::Score : v; if (ss.rdbuf()->in_avail()) // Not at first line ss << "\n"; - ss << "info depth " << d + ss << "info depth " << d / ONE_PLY << " seldepth " << selDepth - << " 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 + << " score " << UCI::value(v); + + if (!tb && i == PVIdx) + ss << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : ""); + + ss << " nodes " << pos.nodes_searched() + << " nps " << pos.nodes_searched() * 1000 / elapsed + << " tbhits " << TB::Hits + << " time " << elapsed << " pv"; - for (size_t j = 0; RootMoves[i].pv[j] != MOVE_NONE; ++j) - ss << " " << move_to_uci(RootMoves[i].pv[j], pos.is_chess960()); + for (size_t j = 0; j < RootMoves[i].pv.size(); ++j) + ss << " " << UCI::move(RootMoves[i].pv[j], pos.is_chess960()); } return ss.str(); @@ -1452,69 +1466,56 @@ moves_loop: // When in check and at SpNode search starts from here } // namespace -/// RootMove::extract_pv_from_tt() builds a PV by adding moves from the TT table. -/// We also consider both failing high nodes and BOUND_EXACT nodes here to -/// ensure that we have a ponder move even when we fail high at root. This -/// results in a long PV to print that is important for position analysis. - -void RootMove::extract_pv_from_tt(Position& pos) { - - StateInfo state[MAX_PLY_PLUS_6], *st = state; - const TTEntry* tte; - int ply = 1; // At root ply is 1... - Move m = pv[0]; // ...instead pv[] array starts from 0 - Value expectedScore = score; - - pv.clear(); - - do { - pv.push_back(m); - - assert(MoveList(pos).contains(pv[ply - 1])); - - pos.do_move(pv[ply++ - 1], *st++); - tte = TT.probe(pos.key()); - expectedScore = -expectedScore; - - } while ( tte - && expectedScore == value_from_tt(tte->value(), ply) - && pos.pseudo_legal(m = tte->move()) // Local copy, TT could change - && pos.legal(m, pos.pinned_pieces(pos.side_to_move())) - && ply < MAX_PLY - && (!pos.is_draw() || ply <= 2)); - - pv.push_back(MOVE_NONE); // Must be zero-terminating - - while (--ply) pos.undo_move(pv[ply - 1]); -} - - /// RootMove::insert_pv_in_tt() is called at the end of a search iteration, and /// inserts the PV back into the TT. This makes sure the old PV moves are searched /// first, even if the old TT entries have been overwritten. void RootMove::insert_pv_in_tt(Position& pos) { - StateInfo state[MAX_PLY_PLUS_6], *st = state; - const TTEntry* tte; - int idx = 0; // Ply starts from 1, we need to start from 0 + StateInfo state[MAX_PLY], *st = state; + size_t idx = 0; - do { - tte = TT.probe(pos.key()); + for ( ; idx < pv.size(); ++idx) + { + bool ttHit; + TTEntry* tte = TT.probe(pos.key(), ttHit); - if (!tte || tte->move() != pv[idx]) // Don't overwrite correct entries - TT.store(pos.key(), VALUE_NONE, BOUND_NONE, DEPTH_NONE, pv[idx], VALUE_NONE); + if (!ttHit || tte->move() != pv[idx]) // Don't overwrite correct entries + tte->save(pos.key(), VALUE_NONE, BOUND_NONE, DEPTH_NONE, pv[idx], VALUE_NONE, TT.generation()); assert(MoveList(pos).contains(pv[idx])); - pos.do_move(pv[idx++], *st++); - - } while (pv[idx] != MOVE_NONE); + pos.do_move(pv[idx], *st++); + } while (idx) pos.undo_move(pv[--idx]); } +/// RootMove::extract_ponder_from_tt() is called in case we have no ponder move before +/// exiting the search, for instance in case we stop the search during a fail high at +/// root. We try hard to have a ponder move to return to the GUI, otherwise in case of +/// 'ponder on' we have nothing to think on. + +Move RootMove::extract_ponder_from_tt(Position& pos) +{ + StateInfo st; + bool found; + + assert(pv.size() == 1); + + pos.do_move(pv[0], st); + TTEntry* tte = TT.probe(pos.key(), found); + Move m = found ? tte->move() : MOVE_NONE; + if (!MoveList(pos).contains(m)) + m = MOVE_NONE; + + pos.undo_move(pv[0]); + pv.push_back(m); + return m; +} + + /// Thread::idle_loop() is where the thread is parked when it has no work to do void Thread::idle_loop() { @@ -1537,7 +1538,7 @@ void Thread::idle_loop() { Threads.mutex.unlock(); - Stack stack[MAX_PLY_PLUS_6], *ss = stack+2; // To allow referencing (ss-2) + Stack stack[MAX_PLY+4], *ss = stack+2; // To allow referencing (ss-2) and (ss+2) Position pos(*sp->pos, this); std::memcpy(ss-2, sp->ss-2, 5 * sizeof(Stack)); @@ -1643,7 +1644,7 @@ void Thread::idle_loop() { void check_time() { static Time::point lastInfoTime = Time::now(); - int64_t nodes = 0; // Workaround silly 'uninitialized' gcc warning + Time::point elapsed = Time::now() - SearchTime; if (Time::now() - lastInfoTime >= 1000) { @@ -1651,14 +1652,28 @@ void check_time() { dbg_print(); } + // An engine may not stop pondering until told so by the GUI if (Limits.ponder) return; - if (Limits.nodes) + if (Limits.use_time_management()) + { + bool stillAtFirstMove = Signals.firstRootMove + && !Signals.failedLowAtRoot + && elapsed > TimeMgr.available_time() * 75 / 100; + + if ( stillAtFirstMove + || elapsed > TimeMgr.maximum_time() - 2 * TimerThread::Resolution) + Signals.stop = true; + } + else if (Limits.movetime && elapsed >= Limits.movetime) + Signals.stop = true; + + else if (Limits.nodes) { Threads.mutex.lock(); - nodes = RootPos.nodes_searched(); + int64_t nodes = RootPos.nodes_searched(); // Loop across all split points and sum accumulated SplitPoint nodes plus // all the currently active positions nodes. @@ -1679,18 +1694,8 @@ void check_time() { } Threads.mutex.unlock(); + + if (nodes >= Limits.nodes) + Signals.stop = true; } - - Time::point elapsed = Time::now() - SearchTime; - bool stillAtFirstMove = Signals.firstRootMove - && !Signals.failedLowAtRoot - && elapsed > TimeMgr.available_time() * 75 / 100; - - bool noMoreTime = elapsed > TimeMgr.maximum_time() - 2 * TimerThread::Resolution - || stillAtFirstMove; - - if ( (Limits.use_time_management() && noMoreTime) - || (Limits.movetime && elapsed >= Limits.movetime) - || (Limits.nodes && nodes >= Limits.nodes)) - Signals.stop = true; } diff --git a/DroidFish/jni/stockfish/search.h b/DroidFish/jni/stockfish/search.h index 4fe5a5b..5a0ad5d 100644 --- a/DroidFish/jni/stockfish/search.h +++ b/DroidFish/jni/stockfish/search.h @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -20,7 +20,7 @@ #ifndef SEARCH_H_INCLUDED #define SEARCH_H_INCLUDED -#include +#include // For std::auto_ptr #include #include @@ -32,12 +32,13 @@ struct SplitPoint; namespace Search { -/// The Stack struct keeps track of the information we need to remember from -/// nodes shallower and deeper in the tree during the search. Each search thread -/// has its own array of Stack objects, indexed by the current ply. +/// Stack struct keeps track of the information we need to remember from nodes +/// shallower and deeper in the tree during the search. Each search thread has +/// its own array of Stack objects, indexed by the current ply. struct Stack { SplitPoint* splitPoint; + Move* pv; int ply; Move currentMove; Move ttMove; @@ -45,49 +46,49 @@ struct Stack { Move killers[2]; Depth reduction; Value staticEval; - bool skipNullMove; + bool skipEarlyPruning; }; +/// RootMove struct is used for moves at the root of the tree. For each root move +/// we store a score and a PV (really a refutation in the case of moves which +/// fail low). Score is normally set at -VALUE_INFINITE for all non-pv moves. -/// RootMove struct is used for moves at the root of the tree. For each root -/// move we store a score, a node count, and a PV (really a refutation in the -/// case of moves which fail low). Score is normally set at -VALUE_INFINITE for -/// all non-pv moves. struct RootMove { - RootMove(Move m) : score(-VALUE_INFINITE), prevScore(-VALUE_INFINITE) { - pv.push_back(m); pv.push_back(MOVE_NONE); - } + RootMove(Move m) : score(-VALUE_INFINITE), previousScore(-VALUE_INFINITE), pv(1, m) {} bool operator<(const RootMove& m) const { return score > m.score; } // Ascending sort bool operator==(const Move& m) const { return pv[0] == m; } - - void extract_pv_from_tt(Position& pos); void insert_pv_in_tt(Position& pos); + Move extract_ponder_from_tt(Position& pos); Value score; - Value prevScore; + Value previousScore; std::vector pv; }; +typedef std::vector RootMoveVector; -/// The LimitsType struct stores information sent by GUI about available time -/// to search the current move, maximum depth/time, if we are in analysis mode -/// or if we have to ponder while it's our opponent's turn to move. +/// LimitsType struct stores information sent by GUI about available time to +/// search the current move, maximum depth/time, if we are in analysis mode or +/// if we have to ponder while it's our opponent's turn to move. struct LimitsType { - LimitsType() { // Using memset on a std::vector is undefined behavior - time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = movestogo = - depth = nodes = movetime = mate = infinite = ponder = 0; + LimitsType() { // Init explicitly due to broken value-initialization of non POD in MSVC + nodes = time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = movestogo = + depth = movetime = mate = infinite = ponder = 0; + } + + bool use_time_management() const { + return !(mate | movetime | depth | nodes | infinite); } - bool use_time_management() const { return !(mate | movetime | depth | nodes | infinite); } std::vector searchmoves; - int time[COLOR_NB], inc[COLOR_NB], movestogo, depth, nodes, movetime, mate, infinite, ponder; + int time[COLOR_NB], inc[COLOR_NB], movestogo, depth, movetime, mate, infinite, ponder; + int64_t nodes; }; - /// The SignalsType struct stores volatile flags updated during the search /// typically in an async fashion e.g. to stop the search by the GUI. @@ -99,13 +100,13 @@ typedef std::auto_ptr > StateStackPtr; extern volatile SignalsType Signals; extern LimitsType Limits; -extern std::vector RootMoves; +extern RootMoveVector RootMoves; extern Position RootPos; extern Time::point SearchTime; extern StateStackPtr SetupStates; -extern void init(); -extern void think(); +void init(); +void think(); template uint64_t perft(Position& pos, Depth depth); } // namespace Search diff --git a/DroidFish/jni/stockfish/syzygy/tbcore.cpp b/DroidFish/jni/stockfish/syzygy/tbcore.cpp new file mode 100644 index 0000000..7b0dae3 --- /dev/null +++ b/DroidFish/jni/stockfish/syzygy/tbcore.cpp @@ -0,0 +1,1381 @@ +/* + 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 +#ifndef _WIN32 +#include +#include +#endif +#include "tbcore.h" + +#define TBMAX_PIECE 254 +#define TBMAX_PAWN 256 +#define HSHMAX 5 + +#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 LOCK_T 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 TB_hash[1 << TBHASHBITS][HSHMAX]; + +#define DTZ_ENTRIES 64 + +static struct DTZTableEntry DTZ_table[DTZ_ENTRIES]; + +static void init_indices(void); +static uint64 calc_key_from_pcs(int *pcs, int 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) +{ + int i; + FD fd; + char file[256]; + + for (i = 0; i < num_paths; i++) { + strcpy(file, paths[i]); + strcat(file, "/"); + strcat(file, str); + strcat(file, suffix); +#ifndef _WIN32 + fd = open(file, O_RDONLY); +#else + fd = CreateFile(file, 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 *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)) { + printf("Could not mmap() %s.\n", name); + exit(1); + } +#else + DWORD size_low, size_high; + size_low = GetFileSize(fd, &size_high); +// *size = ((uint64)size_high) << 32 | ((uint64)size_low); + HANDLE map = CreateFileMapping(fd, NULL, PAGE_READONLY, size_high, size_low, + NULL); + if (map == NULL) { + printf("CreateFileMapping() failed.\n"); + exit(1); + } + *mapping = (uint64)map; + char *data = (char *)MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0); + if (data == NULL) { + printf("MapViewOfFile() failed, name = %s%s, error = %lu.\n", name, suffix, GetLastError()); + exit(1); + } +#endif + close_tb(fd); + return data; +} + +#ifndef _WIN32 +static void unmap_file(char *data, uint64 size) +{ + if (!data) return; + munmap(data, size); +} +#else +static void unmap_file(char *data, uint64 mapping) +{ + if (!data) return; + UnmapViewOfFile(data); + CloseHandle((HANDLE)mapping); +} +#endif + +static void add_to_hash(struct TBEntry *ptr, uint64 key) +{ + int i, hshidx; + + hshidx = key >> (64 - TBHASHBITS); + i = 0; + while (i < HSHMAX && TB_hash[hshidx][i].ptr) + i++; + if (i == HSHMAX) { + printf("HSHMAX too low!\n"); + exit(1); + } else { + TB_hash[hshidx][i].key = key; + TB_hash[hshidx][i].ptr = ptr; + } +} + +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 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, 0); + key2 = calc_key_from_pcs(pcs, 1); + if (pcs[TB_WPAWN] + pcs[TB_BPAWN] == 0) { + if (TBnum_piece == TBMAX_PIECE) { + printf("TBMAX_PIECE limit too low!\n"); + exit(1); + } + entry = (struct TBEntry *)&TB_piece[TBnum_piece++]; + } else { + if (TBnum_pawn == TBMAX_PAWN) { + printf("TBMAX_PAWN limit too low!\n"); + exit(1); + } + entry = (struct TBEntry *)&TB_pawn[TBnum_pawn++]; + } + entry->key = key; + entry->ready = 0; + entry->num = 0; + for (i = 0; i < 16; i++) + entry->num += (ubyte)pcs[i]; + entry->symmetric = (key == key2); + entry->has_pawns = (pcs[TB_WPAWN] + pcs[TB_BPAWN] > 0); + if (entry->num > Tablebases::MaxCardinality) + Tablebases::MaxCardinality = entry->num; + + if (entry->has_pawns) { + struct TBEntry_pawn *ptr = (struct TBEntry_pawn *)entry; + ptr->pawns[0] = (ubyte)pcs[TB_WPAWN]; + ptr->pawns[1] = (ubyte)pcs[TB_BPAWN]; + if (pcs[TB_BPAWN] > 0 + && (pcs[TB_WPAWN] == 0 || pcs[TB_BPAWN] < pcs[TB_WPAWN])) { + ptr->pawns[0] = (ubyte)pcs[TB_BPAWN]; + ptr->pawns[1] = (ubyte)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 = ubyte(1 + j); + } + } + } + add_to_hash(entry, key); + if (key2 != key) add_to_hash(entry, key2); +} + +void Tablebases::init(const std::string& path) +{ + char str[16]; + int i, j, k, l; + + if (initialized) { + free(path_string); + free(paths); + 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 < DTZ_ENTRIES; i++) + if (DTZ_table[i].entry) + free_dtz_entry(DTZ_table[i].entry); + } else { + init_indices(); + initialized = true; + } + + const char *p = path.c_str(); + if (strlen(p) == 0 || !strcmp(p, "")) return; + path_string = (char *)malloc(strlen(p) + 1); + strcpy(path_string, p); + num_paths = 0; + for (i = 0;; i++) { + if (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++; + } + + LOCK_INIT(TB_mutex); + + TBnum_piece = TBnum_pawn = 0; + MaxCardinality = 0; + + for (i = 0; i < (1 << TBHASHBITS); i++) + for (j = 0; j < HSHMAX; j++) { + TB_hash[i][j].key = 0ULL; + TB_hash[i][j].ptr = NULL; + } + + for (i = 0; i < DTZ_ENTRIES; i++) + DTZ_table[i].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); + } + } + + printf("info string Found %d tablebases.\n", TBnum_piece + TBnum_pawn); +} + +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 +}; + +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 } +}; + +static int binomial[5][64]; +static int pawnidx[5][24]; +static int pfactor[5][4]; + +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; + } +} + +static uint64 encode_piece(struct TBEntry_piece *ptr, ubyte *norm, int *pos, int *factor) +{ + uint64 idx; + 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)s) * ((uint64)factor[i]); + i += t; + } + + return idx; +} + +// 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 encode_pawn(struct TBEntry_pawn *ptr, ubyte *norm, int *pos, int *factor) +{ + uint64 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)s) * ((uint64)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)s) * ((uint64)factor[i]); + i += t; + } + + return idx; +} + +// 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 calc_factors_piece(int *factor, int num, int order, ubyte *norm, ubyte enc_type) +{ + int i, k, n; + uint64 f; + static int pivfac[] = { 31332, 28056, 462 }; + + n = 64 - norm[0]; + + f = 1; + for (i = norm[0], k = 0; i < num || k == order; k++) { + if (k == order) { + factor[0] = static_cast(f); + f *= pivfac[enc_type]; + } else { + factor[i] = static_cast(f); + f *= subfactor(norm[i], n); + n -= norm[i]; + i += norm[i]; + } + } + + return f; +} + +static uint64 calc_factors_pawn(int *factor, int num, int order, int order2, ubyte *norm, int file) +{ + int i, k, n; + uint64 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] = static_cast(f); + f *= pfactor[norm[0] - 1][file]; + } else if (k == order2) { + factor[norm[0]] = static_cast(f); + f *= subfactor(norm[norm[0]], 48 - norm[0]); + } else { + factor[i] = static_cast(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] = ubyte(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 *tb_size) +{ + int i; + int order; + + for (i = 0; i < ptr->num; i++) + ptr->pieces[0][i] = ubyte(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] = ubyte(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 *tb_size) +{ + int i; + int order; + + for (i = 0; i < ptr->num; i++) + ptr->pieces[i] = ubyte(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 *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] = ubyte(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] = ubyte(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 *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] = ubyte(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; + + ubyte* w = d->sympat + 3 * s; + s2 = (w[2] << 4) | (w[1] >> 4); + if (s2 == 0x0fff) + d->symlen[s] = 0; + else { + s1 = ((w[1] & 0xf) << 8) | w[0]; + if (!tmp[s1]) calc_symlen(d, s1, tmp); + if (!tmp[s2]) calc_symlen(d, s2, tmp); + d->symlen[s] = ubyte(d->symlen[s1] + d->symlen[s2] + 1); + } + tmp[s] = 1; +} + +ushort ReadUshort(ubyte* d) { + return ushort(d[0] | (d[1] << 8)); +} + +uint32 ReadUint32(ubyte* d) { + return d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24); +} + +static struct PairsData *setup_pairs(unsigned char *data, uint64 tb_size, uint64 *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 = ReadUint32(&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 = ReadUshort(&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)]; + + uint64 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] + ReadUshort((ubyte*)(d->offset + i)) - ReadUshort((ubyte*)(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, char *str) +{ + ubyte *next; + int f, s; + uint64 tb_size[8]; + uint64 size[8 * 3]; + ubyte flags; + + // first mmap the table into memory + + entry->data = map_file(str, WDLSUFFIX, &entry->mapping); + if (!entry->data) { + printf("Could not find %s" WDLSUFFIX, str); + return 0; + } + + ubyte *data = (ubyte *)entry->data; + if (data[0] != WDL_MAGIC[0] || + data[1] != WDL_MAGIC[1] || + data[2] != WDL_MAGIC[2] || + data[3] != WDL_MAGIC[3]) { + printf("Corrupted table.\n"); + 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 tb_size[4]; + uint64 size[4 * 3]; + + if (!data) + return 0; + + if (data[0] != DTZ_MAGIC[0] || + data[1] != DTZ_MAGIC[1] || + data[2] != DTZ_MAGIC[2] || + data[3] != DTZ_MAGIC[3]) { + printf("Corrupted table.\n"); + 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] = static_cast(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] = static_cast(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; +} + +template +static ubyte decompress_pairs(struct PairsData *d, uint64 idx) +{ + if (!d->idxbits) + return ubyte(d->min_len); + + uint32 mainidx = static_cast(idx >> d->idxbits); + int litidx = (idx & ((1ULL << d->idxbits) - 1)) - (1ULL << (d->idxbits - 1)); + uint32 block = *(uint32 *)(d->indextable + 6 * mainidx); + if (!LittleEndian) + block = BSWAP32(block); + + ushort idxOffset = *(ushort *)(d->indextable + 6 * mainidx + 4); + if (!LittleEndian) + idxOffset = ushort((idxOffset << 8) | (idxOffset >> 8)); + litidx += idxOffset; + + if (litidx < 0) { + do { + litidx += d->sizetable[--block] + 1; + } while (litidx < 0); + } else { + while (litidx > d->sizetable[block]) + litidx -= d->sizetable[block++] + 1; + } + + uint32 *ptr = (uint32 *)(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 code = *((uint64 *)ptr); + if (LittleEndian) + code = BSWAP64(code); + + ptr += 2; + bitcnt = 0; // number of "empty bits" in code + for (;;) { + int l = m; + while (code < base[l]) l++; + sym = offset[l]; + if (!LittleEndian) + sym = ((sym & 0xff) << 8) | (sym >> 8); + sym += static_cast((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; + uint32 tmp = *ptr++; + if (LittleEndian) + tmp = BSWAP32(tmp); + code |= ((uint64)tmp) << bitcnt; + } + } + + ubyte *sympat = d->sympat; + while (symlen[sym] != 0) { + ubyte* w = sympat + (3 * sym); + int s1 = ((w[1] & 0xf) << 8) | w[0]; + if (litidx < (int)symlen[s1] + 1) + sym = s1; + else { + litidx -= (int)symlen[s1] + 1; + sym = (w[2] << 4) | (w[1] >> 4); + } + } + + return sympat[3 * sym]; +} + +void load_dtz_table(char *str, uint64 key1, uint64 key2) +{ + int i; + struct TBEntry *ptr, *ptr3; + struct TBHashEntry *ptr2; + + DTZ_table[0].key1 = key1; + DTZ_table[0].key2 = key2; + DTZ_table[0].entry = NULL; + + // find corresponding WDL entry + ptr2 = TB_hash[key1 >> (64 - TBHASHBITS)]; + for (i = 0; i < HSHMAX; i++) + if (ptr2[i].key == key1) break; + if (i == HSHMAX) return; + 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); + else + DTZ_table[0].entry = ptr3; +} + +static void free_wdl_entry(struct TBEntry *entry) +{ + unmap_file(entry->data, entry->mapping); + if (!entry->has_pawns) { + struct TBEntry_piece *ptr = (struct TBEntry_piece *)entry; + free(ptr->precomp[0]); + if (ptr->precomp[1]) + free(ptr->precomp[1]); + } else { + struct TBEntry_pawn *ptr = (struct TBEntry_pawn *)entry; + int f; + for (f = 0; f < 4; f++) { + free(ptr->file[f].precomp[0]); + if (ptr->file[f].precomp[1]) + free(ptr->file[f].precomp[1]); + } + } +} + +static void free_dtz_entry(struct TBEntry *entry) +{ + unmap_file(entry->data, entry->mapping); + if (!entry->has_pawns) { + struct DTZEntry_piece *ptr = (struct DTZEntry_piece *)entry; + free(ptr->precomp); + } else { + struct DTZEntry_pawn *ptr = (struct DTZEntry_pawn *)entry; + int f; + for (f = 0; f < 4; f++) + free(ptr->file[f].precomp); + } + 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/syzygy/tbcore.h b/DroidFish/jni/stockfish/syzygy/tbcore.h new file mode 100644 index 0000000..cdaf2ac --- /dev/null +++ b/DroidFish/jni/stockfish/syzygy/tbcore.h @@ -0,0 +1,169 @@ +/* + Copyright (c) 2011-2013 Ronald de Man +*/ + +#ifndef TBCORE_H +#define TBCORE_H + +#ifndef _WIN32 +#include +#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 + +#ifndef _WIN32 +#define LOCK_T pthread_mutex_t +#define LOCK_INIT(x) pthread_mutex_init(&(x), NULL) +#define LOCK(x) pthread_mutex_lock(&(x)) +#define UNLOCK(x) pthread_mutex_unlock(&(x)) +#else +#define LOCK_T HANDLE +#define LOCK_INIT(x) do { x = CreateMutex(NULL, FALSE, NULL); } while (0) +#define LOCK(x) WaitForSingleObject(x, INFINITE) +#define UNLOCK(x) ReleaseMutex(x) +#endif + +#ifndef _MSC_VER +#define BSWAP32(v) __builtin_bswap32(v) +#define BSWAP64(v) __builtin_bswap64(v) +#else +#define BSWAP32(v) _byteswap_ulong(v) +#define BSWAP64(v) _byteswap_uint64(v) +#endif + +#define WDLSUFFIX ".rtbw" +#define DTZSUFFIX ".rtbz" +#define WDLDIR "RTBWDIR" +#define DTZDIR "RTBZDIR" +#define TBPIECES 6 + +typedef unsigned long long uint64; +typedef unsigned int uint32; +typedef unsigned char ubyte; +typedef unsigned short ushort; + +const ubyte WDL_MAGIC[4] = { 0x71, 0xe8, 0x23, 0x5d }; +const ubyte DTZ_MAGIC[4] = { 0xd7, 0x66, 0x0c, 0xa5 }; + +#define TBHASHBITS 10 + +struct TBHashEntry; + +typedef uint64 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 key; + uint64 mapping; + ubyte ready; + ubyte num; + ubyte symmetric; + ubyte has_pawns; +} +#ifndef _WIN32 +__attribute__((__may_alias__)) +#endif +; + +struct TBEntry_piece { + char *data; + uint64 key; + uint64 mapping; + ubyte ready; + ubyte num; + ubyte symmetric; + ubyte has_pawns; + ubyte enc_type; + struct PairsData *precomp[2]; + int factor[2][TBPIECES]; + ubyte pieces[2][TBPIECES]; + ubyte norm[2][TBPIECES]; +}; + +struct TBEntry_pawn { + char *data; + uint64 key; + uint64 mapping; + ubyte ready; + ubyte num; + ubyte symmetric; + ubyte has_pawns; + ubyte pawns[2]; + struct { + struct PairsData *precomp[2]; + int factor[2][TBPIECES]; + ubyte pieces[2][TBPIECES]; + ubyte norm[2][TBPIECES]; + } file[4]; +}; + +struct DTZEntry_piece { + char *data; + uint64 key; + uint64 mapping; + ubyte ready; + ubyte num; + ubyte symmetric; + ubyte has_pawns; + ubyte enc_type; + struct PairsData *precomp; + int factor[TBPIECES]; + ubyte pieces[TBPIECES]; + ubyte norm[TBPIECES]; + ubyte flags; // accurate, mapped, side + ushort map_idx[4]; + ubyte *map; +}; + +struct DTZEntry_pawn { + char *data; + uint64 key; + uint64 mapping; + ubyte ready; + ubyte num; + ubyte symmetric; + ubyte has_pawns; + ubyte pawns[2]; + struct { + struct PairsData *precomp; + int factor[TBPIECES]; + ubyte pieces[TBPIECES]; + ubyte norm[TBPIECES]; + } file[4]; + ubyte flags[4]; + ushort map_idx[4][4]; + ubyte *map; +}; + +struct TBHashEntry { + uint64 key; + struct TBEntry *ptr; +}; + +struct DTZTableEntry { + uint64 key1; + uint64 key2; + struct TBEntry *entry; +}; + +#endif + diff --git a/DroidFish/jni/stockfish/syzygy/tbprobe.cpp b/DroidFish/jni/stockfish/syzygy/tbprobe.cpp new file mode 100644 index 0000000..0abd2b2 --- /dev/null +++ b/DroidFish/jni/stockfish/syzygy/tbprobe.cpp @@ -0,0 +1,831 @@ +/* + 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 + +#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::MaxCardinality = 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 == 0 and the black pieces if mirror == 1. +static void prt_str(Position& pos, char *str, int 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 calc_key(Position& pos, int mirror) +{ + Color color; + PieceType pt; + int i; + uint64 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 calc_key_from_pcs(int *pcs, int mirror) +{ + int color; + PieceType pt; + int i; + uint64 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; +} + +bool is_little_endian() { + union { + int i; + char c[sizeof(int)]; + } x; + x.i = 1; + return x.c[0] == 1; +} + +static ubyte decompress_pairs(struct PairsData *d, uint64 idx) +{ + static const bool isLittleEndian = is_little_endian(); + return isLittleEndian ? decompress_pairs(d, idx) + : decompress_pairs(d, idx); +} + +// 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 idx; + uint64 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 = TB_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; + if (!ptr->ready) { + LOCK(TB_mutex); + if (!ptr->ready) { + char str[16]; + prt_str(pos, str, ptr->key != key); + if (!init_table_wdl(ptr, str)) { + ptr2[i].key = 0ULL; + *success = 0; + UNLOCK(TB_mutex); + return 0; + } + // Memory barrier to ensure ptr->ready = 1 is not reordered. +#ifdef _MSC_VER + _ReadWriteBarrier(); +#else + __asm__ __volatile__ ("" ::: "memory"); +#endif + ptr->ready = 1; + } + UNLOCK(TB_mutex); + } + + 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) +{ + struct TBEntry *ptr; + uint64 idx; + int i, res; + int p[TBPIECES]; + + // Obtain the position's material signature key. + uint64 key = pos.material_key(); + + if (DTZ_table[0].key1 != key && DTZ_table[0].key2 != key) { + for (i = 1; i < DTZ_ENTRIES; i++) + if (DTZ_table[i].key1 == key) break; + if (i < DTZ_ENTRIES) { + struct DTZTableEntry table_entry = DTZ_table[i]; + for (; i > 0; i--) + DTZ_table[i] = DTZ_table[i - 1]; + DTZ_table[0] = table_entry; + } else { + struct TBHashEntry *ptr2 = TB_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; + char str[16]; + int mirror = (ptr->key != key); + prt_str(pos, str, mirror); + if (DTZ_table[DTZ_ENTRIES - 1].entry) + free_dtz_entry(DTZ_table[DTZ_ENTRIES-1].entry); + for (i = DTZ_ENTRIES - 1; i > 0; i--) + DTZ_table[i] = DTZ_table[i - 1]; + load_dtz_table(str, calc_key(pos, mirror), calc_key(pos, !mirror)); + } + } + + ptr = DTZ_table[0].entry; + 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, Search::RootMoveVector& rootMoves, Value& score) +{ + 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 < rootMoves.size(); i++) { + Move move = 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; + 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. + score = 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) + score = (Value)(((200 - dtz - cnt50) * int(PawnValueEg)) / 200); + else if (wdl == -1 && dtz >= -100) + score = -(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 < rootMoves.size(); i++) { + int v = 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 < rootMoves.size(); i++) { + int v = rootMoves[i].score; + if (v > 0 && v <= max) + rootMoves[j++] = rootMoves[i]; + } + } else if (dtz < 0) { // losing (or 50-move rule draw) + int best = 0; + for (size_t i = 0; i < rootMoves.size(); i++) { + int v = 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 < rootMoves.size(); i++) { + if (rootMoves[i].score == best) + rootMoves[j++] = rootMoves[i]; + } + } else { // drawing + // Try all moves that preserve the draw. + for (size_t i = 0; i < rootMoves.size(); i++) { + if (rootMoves[i].score == 0) + rootMoves[j++] = rootMoves[i]; + } + } + 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, Search::RootMoveVector& rootMoves, Value& score) +{ + int success; + + int wdl = Tablebases::probe_wdl(pos, &success); + if (!success) return false; + score = wdl_to_Value[wdl + 2]; + + StateInfo st; + CheckInfo ci(pos); + + int best = -2; + + // Probe each move. + for (size_t i = 0; i < rootMoves.size(); i++) { + Move move = 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; + rootMoves[i].score = (Value)v; + if (v > best) + best = v; + } + + size_t j = 0; + for (size_t i = 0; i < rootMoves.size(); i++) { + if (rootMoves[i].score == best) + rootMoves[j++] = rootMoves[i]; + } + rootMoves.resize(j, Search::RootMove(MOVE_NONE)); + + return true; +} + diff --git a/DroidFish/jni/stockfish/syzygy/tbprobe.h b/DroidFish/jni/stockfish/syzygy/tbprobe.h new file mode 100644 index 0000000..4233e1a --- /dev/null +++ b/DroidFish/jni/stockfish/syzygy/tbprobe.h @@ -0,0 +1,18 @@ +#ifndef TBPROBE_H +#define TBPROBE_H + +#include "../search.h" + +namespace Tablebases { + +extern int MaxCardinality; + +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, Search::RootMoveVector& rootMoves, Value& score); +bool root_probe_wdl(Position& pos, Search::RootMoveVector& rootMoves, Value& score); + +} + +#endif diff --git a/DroidFish/jni/stockfish/tbcore.cpp b/DroidFish/jni/stockfish/tbcore.cpp deleted file mode 100644 index 6a2e814..0000000 --- a/DroidFish/jni/stockfish/tbcore.cpp +++ /dev/null @@ -1,1620 +0,0 @@ -/* - 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 deleted file mode 100644 index f9c2d56..0000000 --- a/DroidFish/jni/stockfish/tbcore.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - 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 deleted file mode 100644 index fa79e51..0000000 --- a/DroidFish/jni/stockfish/tbprobe.cpp +++ /dev/null @@ -1,819 +0,0 @@ -/* - 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 deleted file mode 100644 index 9ed6f0a..0000000 --- a/DroidFish/jni/stockfish/tbprobe.h +++ /dev/null @@ -1,16 +0,0 @@ -#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 3b98ac6..b8571dc 100644 --- a/DroidFish/jni/stockfish/thread.cpp +++ b/DroidFish/jni/stockfish/thread.cpp @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -23,7 +23,7 @@ #include "movegen.h" #include "search.h" #include "thread.h" -#include "ucioption.h" +#include "uci.h" using namespace Search; @@ -40,7 +40,7 @@ namespace { // Helpers to launch a thread after creation and joining before delete. Must be - // outside Thread c'tor and d'tor because the object will be fully initialized + // outside Thread c'tor and d'tor because the object must be fully initialized // when start_routine (and hence virtual idle_loop) is called and when joining. template T* new_thread() { @@ -50,7 +50,11 @@ namespace { } void delete_thread(ThreadBase* th) { + + th->mutex.lock(); th->exit = true; // Search must be already finished + th->mutex.unlock(); + th->notify_one(); thread_join(th->handle); // Wait for thread termination delete th; @@ -59,7 +63,7 @@ namespace { } -// notify_one() wakes up the thread when there is some work to do +// ThreadBase::notify_one() wakes up the thread when there is some work to do void ThreadBase::notify_one() { @@ -69,20 +73,20 @@ void ThreadBase::notify_one() { } -// wait_for() set the thread to sleep until condition 'b' turns true +// ThreadBase::wait_for() set the thread to sleep until 'condition' turns true -void ThreadBase::wait_for(volatile const bool& b) { +void ThreadBase::wait_for(volatile const bool& condition) { mutex.lock(); - while (!b) sleepCondition.wait(mutex); + while (!condition) sleepCondition.wait(mutex); mutex.unlock(); } -// Thread c'tor just inits data and does not launch any execution thread. -// Such a thread will only be started when c'tor returns. +// Thread c'tor makes some init but does not launch any execution thread that +// will be started only when c'tor returns. -Thread::Thread() /* : splitPoints() */ { // Value-initialization bug in MSVC +Thread::Thread() /* : splitPoints() */ { // Initialization of non POD broken in MSVC searching = false; maxPly = splitPointsSize = 0; @@ -92,7 +96,7 @@ Thread::Thread() /* : splitPoints() */ { // Value-initialization bug in MSVC } -// cutoff_occurred() checks whether a beta cutoff has occurred in the +// Thread::cutoff_occurred() checks whether a beta cutoff has occurred in the // current active split point, or in some ancestor of the split point. bool Thread::cutoff_occurred() const { @@ -127,145 +131,25 @@ bool Thread::available_to(const Thread* master) const { } -// TimerThread::idle_loop() is where the timer thread waits msec milliseconds -// and then calls check_time(). If msec is 0 thread sleeps until it's woken up. - -void TimerThread::idle_loop() { - - while (!exit) - { - mutex.lock(); - - if (!exit) - sleepCondition.wait_for(mutex, run ? Resolution : INT_MAX); - - mutex.unlock(); - - if (run) - check_time(); - } -} - - -// MainThread::idle_loop() is where the main thread is parked waiting to be started -// when there is a new search. The main thread will launch all the slave threads. - -void MainThread::idle_loop() { - - while (true) - { - mutex.lock(); - - thinking = false; - - while (!thinking && !exit) - { - Threads.sleepCondition.notify_one(); // Wake up the UI thread if needed - sleepCondition.wait(mutex); - } - - mutex.unlock(); - - if (exit) - return; - - searching = true; - - Search::think(); - - assert(searching); - - searching = false; - } -} - - -// init() is called at startup to create and launch requested threads, that will -// go immediately to sleep. We cannot use a c'tor because Threads is a static -// object and we need a fully initialized engine at this point due to allocation -// of Endgames in Thread c'tor. - -void ThreadPool::init() { - - timer = new_thread(); - push_back(new_thread()); - read_uci_options(); -} - - -// exit() cleanly terminates the threads before the program exits. Cannot be done in -// d'tor because we have to terminate the threads before to free ThreadPool object. - -void ThreadPool::exit() { - - delete_thread(timer); // As first because check_time() accesses threads data - - for (iterator it = begin(); it != end(); ++it) - delete_thread(*it); -} - - -// read_uci_options() updates internal threads parameters from the corresponding -// UCI options and creates/destroys threads to match the requested number. Thread -// objects are dynamically allocated to avoid creating all possible threads -// in advance (which include pawns and material tables), even if only a few -// are to be used. - -void ThreadPool::read_uci_options() { - - minimumSplitDepth = Options["Min Split Depth"] * ONE_PLY; - size_t requested = Options["Threads"]; - - assert(requested > 0); - - // If zero (default) then set best minimum split depth automatically - if (!minimumSplitDepth) - minimumSplitDepth = requested < 8 ? 4 * ONE_PLY : 7 * ONE_PLY; - - while (size() < requested) - push_back(new_thread()); - - while (size() > requested) - { - delete_thread(back()); - pop_back(); - } -} - - -// available_slave() tries to find an idle thread which is available as a slave -// for the thread 'master'. - -Thread* ThreadPool::available_slave(const Thread* master) const { - - for (const_iterator it = begin(); it != end(); ++it) - if ((*it)->available_to(master)) - return *it; - - return NULL; -} - - -// split() does the actual work of distributing the work at a node between +// Thread::split() does the actual work of distributing the work at a node between // several available threads. If it does not succeed in splitting the node // (because no idle threads are available), the function immediately returns. // If splitting is possible, a SplitPoint object is initialized with all the // data that must be copied to the helper threads and then helper threads are -// told that they have been assigned work. This will cause them to instantly +// informed that they have been assigned work. This will cause them to instantly // leave their idle loops and call search(). When all threads have returned from // search() then split() returns. -void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Value* bestValue, +void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bestValue, Move* bestMove, Depth depth, int moveCount, MovePicker* movePicker, int nodeType, bool cutNode) { - assert(pos.pos_is_ok()); + assert(searching); assert(-VALUE_INFINITE < *bestValue && *bestValue <= alpha && alpha < beta && beta <= VALUE_INFINITE); assert(depth >= Threads.minimumSplitDepth); - assert(searching); assert(splitPointsSize < MAX_SPLITPOINTS_PER_THREAD); - // Pick the next available split point from the split point stack + // Pick and init the next available split point SplitPoint& sp = splitPoints[splitPointsSize]; sp.masterThread = this; @@ -296,7 +180,9 @@ void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Valu activeSplitPoint = &sp; activePosition = NULL; - for (Thread* slave; (slave = Threads.available_slave(this)) != NULL; ) + Thread* slave; + + while ((slave = Threads.available_slave(this)) != NULL) { sp.slavesMask.set(slave->idx); slave->activeSplitPoint = &sp; @@ -320,8 +206,8 @@ void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Valu assert(!activePosition); // We have returned from the idle loop, which means that all threads are - // finished. Note that setting 'searching' and decreasing splitPointsSize is - // done under lock protection to avoid a race with Thread::available_to(). + // finished. Note that setting 'searching' and decreasing splitPointsSize must + // be done under lock protection to avoid a race with Thread::available_to(). Threads.mutex.lock(); sp.mutex.lock(); @@ -337,22 +223,142 @@ void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Valu Threads.mutex.unlock(); } -// wait_for_think_finished() waits for main thread to go to sleep then returns -void ThreadPool::wait_for_think_finished() { +// TimerThread::idle_loop() is where the timer thread waits Resolution milliseconds +// and then calls check_time(). When not searching, thread sleeps until it's woken up. - MainThread* t = main(); - t->mutex.lock(); - while (t->thinking) sleepCondition.wait(t->mutex); - t->mutex.unlock(); +void TimerThread::idle_loop() { + + while (!exit) + { + mutex.lock(); + + if (!exit) + sleepCondition.wait_for(mutex, run ? Resolution : INT_MAX); + + mutex.unlock(); + + if (run) + check_time(); + } } -// start_thinking() wakes up the main thread sleeping in MainThread::idle_loop() -// so to start a new search, then returns immediately. +// MainThread::idle_loop() is where the main thread is parked waiting to be started +// when there is a new search. The main thread will launch all the slave threads. -void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits, StateStackPtr& states) { +void MainThread::idle_loop() { + while (!exit) + { + mutex.lock(); + + thinking = false; + + while (!thinking && !exit) + { + Threads.sleepCondition.notify_one(); // Wake up the UI thread if needed + sleepCondition.wait(mutex); + } + + mutex.unlock(); + + if (!exit) + { + searching = true; + + Search::think(); + + assert(searching); + + searching = false; + } + } +} + + +// ThreadPool::init() is called at startup to create and launch requested threads, +// that will go immediately to sleep. We cannot use a c'tor because Threads is a +// static object and we need a fully initialized engine at this point due to +// allocation of Endgames in Thread c'tor. + +void ThreadPool::init() { + + timer = new_thread(); + push_back(new_thread()); + read_uci_options(); +} + + +// ThreadPool::exit() terminates the threads before the program exits. Cannot be +// done in d'tor because threads must be terminated before freeing us. + +void ThreadPool::exit() { + + delete_thread(timer); // As first because check_time() accesses threads data + + for (iterator it = begin(); it != end(); ++it) + delete_thread(*it); +} + + +// ThreadPool::read_uci_options() updates internal threads parameters from the +// corresponding UCI options and creates/destroys threads to match the requested +// number. Thread objects are dynamically allocated to avoid creating all possible +// threads in advance (which include pawns and material tables), even if only a +// few are to be used. + +void ThreadPool::read_uci_options() { + + minimumSplitDepth = Options["Min Split Depth"] * ONE_PLY; + size_t requested = Options["Threads"]; + + assert(requested > 0); + + // If zero (default) then set best minimum split depth automatically + if (!minimumSplitDepth) + minimumSplitDepth = requested < 8 ? 4 * ONE_PLY : 7 * ONE_PLY; + + while (size() < requested) + push_back(new_thread()); + + while (size() > requested) + { + delete_thread(back()); + pop_back(); + } +} + + +// ThreadPool::available_slave() tries to find an idle thread which is available +// as a slave for the thread 'master'. + +Thread* ThreadPool::available_slave(const Thread* master) const { + + for (const_iterator it = begin(); it != end(); ++it) + if ((*it)->available_to(master)) + return *it; + + return NULL; +} + + +// ThreadPool::wait_for_think_finished() waits for main thread to finish the search + +void ThreadPool::wait_for_think_finished() { + + MainThread* th = main(); + th->mutex.lock(); + while (th->thinking) sleepCondition.wait(th->mutex); + th->mutex.unlock(); +} + + +// ThreadPool::start_thinking() wakes up the main thread sleeping in +// MainThread::idle_loop() and starts a new search, then returns immediately. + +void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits, + StateStackPtr& states) { wait_for_think_finished(); SearchTime = Time::now(); // As early as possible diff --git a/DroidFish/jni/stockfish/thread.h b/DroidFish/jni/stockfish/thread.h index 26aed39..9750ed7 100644 --- a/DroidFish/jni/stockfish/thread.h +++ b/DroidFish/jni/stockfish/thread.h @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -29,9 +29,14 @@ #include "position.h" #include "search.h" +struct Thread; + const int MAX_THREADS = 128; const int MAX_SPLITPOINTS_PER_THREAD = 8; +/// Mutex and ConditionVariable struct are wrappers of the low level locking +/// machinery and are modeled after the corresponding C++11 classes. + struct Mutex { Mutex() { lock_init(l); } ~Mutex() { lock_destroy(l); } @@ -57,13 +62,15 @@ private: WaitCondition c; }; -struct Thread; + +/// SplitPoint struct stores information shared by the threads searching in +/// parallel below the same split point. It is populated at splitting time. struct SplitPoint { // Const data after split point has been setup const Position* pos; - const Search::Stack* ss; + Search::Stack* ss; Thread* masterThread; Depth depth; Value beta; @@ -74,7 +81,7 @@ struct SplitPoint { MovePicker* movePicker; SplitPoint* parentSplitPoint; - // Shared data + // Shared variable data Mutex mutex; std::bitset slavesMask; volatile bool allSlavesSearching; @@ -117,13 +124,13 @@ struct Thread : public ThreadBase { bool cutoff_occurred() const; bool available_to(const Thread* master) const; - void split(Position& pos, const Search::Stack* ss, Value alpha, Value beta, Value* bestValue, Move* bestMove, + void split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value* bestValue, Move* bestMove, Depth depth, int moveCount, MovePicker* movePicker, int nodeType, bool cutNode); SplitPoint splitPoints[MAX_SPLITPOINTS_PER_THREAD]; + Pawns::Table pawnsTable; Material::Table materialTable; Endgames endgames; - Pawns::Table pawnsTable; Position* activePosition; size_t idx; int maxPly; @@ -143,10 +150,13 @@ struct MainThread : public Thread { }; struct TimerThread : public ThreadBase { + + static const int Resolution = 5; // Millisec between two check_time() calls + TimerThread() : run(false) {} virtual void idle_loop(); + bool run; - static const int Resolution = 5; // msec between two check_time() calls }; @@ -156,10 +166,10 @@ struct TimerThread : public ThreadBase { struct ThreadPool : public std::vector { - void init(); // No c'tor and d'tor, threads rely on globals that should - void exit(); // be initialized and are valid during the whole thread lifetime. + void init(); // No c'tor and d'tor, threads rely on globals that should be + void exit(); // initialized and are valid during the whole thread lifetime. - MainThread* main() { return static_cast((*this)[0]); } + MainThread* main() { return static_cast(at(0)); } void read_uci_options(); Thread* available_slave(const Thread* master) const; void wait_for_think_finished(); diff --git a/DroidFish/jni/stockfish/timeman.cpp b/DroidFish/jni/stockfish/timeman.cpp index 88a52bb..04730ab 100644 --- a/DroidFish/jni/stockfish/timeman.cpp +++ b/DroidFish/jni/stockfish/timeman.cpp @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -23,7 +23,7 @@ #include "search.h" #include "timeman.h" -#include "ucioption.h" +#include "uci.h" namespace { @@ -33,10 +33,6 @@ namespace { const double MaxRatio = 7.0; // When in trouble, we can step over reserved time with this ratio const double StealRatio = 0.33; // However we must not steal time from remaining moves over this ratio - const double xscale = 9.3; - const double xshift = 59.8; - const double skewfactor = 0.172; - // move_importance() is a skew-logistic function based on naive statistical // analysis of "how many games are still undecided after n half-moves". Game @@ -45,79 +41,76 @@ namespace { double move_importance(int ply) { - return pow((1 + exp((ply - xshift) / xscale)), -skewfactor) + DBL_MIN; // Ensure non-zero + const double XScale = 9.3; + const double XShift = 59.8; + const double Skew = 0.172; + + return pow((1 + exp((ply - XShift) / XScale)), -Skew) + DBL_MIN; // Ensure non-zero } template - int remaining(int myTime, int movesToGo, int currentPly, int slowMover) + int remaining(int myTime, int movesToGo, int ply, int slowMover) { const double TMaxRatio = (T == OptimumTime ? 1 : MaxRatio); const double TStealRatio = (T == OptimumTime ? 0 : StealRatio); - double thisMoveImportance = (move_importance(currentPly) * slowMover) / 100; + double moveImportance = (move_importance(ply) * slowMover) / 100; double otherMovesImportance = 0; for (int i = 1; i < movesToGo; ++i) - otherMovesImportance += move_importance(currentPly + 2 * i); + otherMovesImportance += move_importance(ply + 2 * i); - double ratio1 = (TMaxRatio * thisMoveImportance) / (TMaxRatio * thisMoveImportance + otherMovesImportance); - double ratio2 = (thisMoveImportance + TStealRatio * otherMovesImportance) / (thisMoveImportance + otherMovesImportance); + double ratio1 = (TMaxRatio * moveImportance) / (TMaxRatio * moveImportance + otherMovesImportance); + double ratio2 = (moveImportance + TStealRatio * otherMovesImportance) / (moveImportance + otherMovesImportance); - return int(myTime * std::min(ratio1, ratio2)); + return int(myTime * std::min(ratio1, ratio2)); // Intel C++ asks an explicit cast } } // namespace -void TimeManager::init(const Search::LimitsType& limits, int currentPly, Color us) +/// init() is called at the beginning of the search and calculates the allowed +/// thinking time out of the time control and current game ply. We support four +/// different kinds of time controls, passed in 'limits': +/// +/// inc == 0 && movestogo == 0 means: x basetime [sudden death!] +/// inc == 0 && movestogo != 0 means: x moves in y minutes +/// inc > 0 && movestogo == 0 means: x basetime + z increment +/// inc > 0 && movestogo != 0 means: x moves in y minutes + z increment + +void TimeManager::init(const Search::LimitsType& limits, Color us, int ply) { - /* We support four different kinds of time controls: - - increment == 0 && movesToGo == 0 means: x basetime [sudden death!] - increment == 0 && movesToGo != 0 means: x moves in y minutes - 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 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 - emergencyMoveTime : Plus attempt to keep at least this much time for each remaining emergency move - minThinkingTime : No matter what, use at least this much thinking before doing the move - */ - - int hypMTG, hypMyTime, t1, t2; - - // Read uci parameters - int moveOverhead = Options["Move Overhead"]; int minThinkingTime = Options["Minimum Thinking Time"]; + int moveOverhead = Options["Move Overhead"]; int slowMover = Options["Slow Mover"]; // Initialize unstablePvFactor to 1 and search times to maximum values unstablePvFactor = 1; optimumSearchTime = maximumSearchTime = std::max(limits.time[us], minThinkingTime); - // We calculate optimum time usage for different hypothetical "moves to go"-values and choose the - // minimum of calculated search time values. Usually the greatest hypMTG gives the minimum values. - for (hypMTG = 1; hypMTG <= (limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon); ++hypMTG) + const int MaxMTG = limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon; + + // We calculate optimum time usage for different hypothetical "moves to go"-values + // and choose the minimum of calculated search time values. Usually the greatest + // hypMTG gives the minimum values. + for (int hypMTG = 1; hypMTG <= MaxMTG; ++hypMTG) { // Calculate thinking time for hypothetical "moves to go"-value - hypMyTime = limits.time[us] - + limits.inc[us] * (hypMTG - 1) - - moveOverhead * (2 + std::min(hypMTG, 40)); + int hypMyTime = limits.time[us] + + limits.inc[us] * (hypMTG - 1) + - moveOverhead * (2 + std::min(hypMTG, 40)); hypMyTime = std::max(hypMyTime, 0); - t1 = minThinkingTime + remaining(hypMyTime, hypMTG, currentPly, slowMover); - t2 = minThinkingTime + remaining(hypMyTime, hypMTG, currentPly, slowMover); + int t1 = minThinkingTime + remaining(hypMyTime, hypMTG, ply, slowMover); + int t2 = minThinkingTime + remaining(hypMyTime, hypMTG, ply, slowMover); - optimumSearchTime = std::min(optimumSearchTime, t1); - maximumSearchTime = std::min(maximumSearchTime, t2); + optimumSearchTime = std::min(t1, optimumSearchTime); + maximumSearchTime = std::min(t2, maximumSearchTime); } if (Options["Ponder"]) optimumSearchTime += optimumSearchTime / 4; - // Make sure that maxSearchTime is not over absoluteMaxSearchTime optimumSearchTime = std::min(optimumSearchTime, maximumSearchTime); } diff --git a/DroidFish/jni/stockfish/timeman.h b/DroidFish/jni/stockfish/timeman.h index a15551a..b904e1c 100644 --- a/DroidFish/jni/stockfish/timeman.h +++ b/DroidFish/jni/stockfish/timeman.h @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -25,7 +25,7 @@ class TimeManager { public: - void init(const Search::LimitsType& limits, int currentPly, Color us); + void init(const Search::LimitsType& limits, Color us, int ply); void pv_instability(double bestMoveChanges) { unstablePvFactor = 1 + bestMoveChanges; } int available_time() const { return int(optimumSearchTime * unstablePvFactor * 0.71); } int maximum_time() const { return maximumSearchTime; } diff --git a/DroidFish/jni/stockfish/tt.cpp b/DroidFish/jni/stockfish/tt.cpp index 46d891c..66113ee 100644 --- a/DroidFish/jni/stockfish/tt.cpp +++ b/DroidFish/jni/stockfish/tt.cpp @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -17,7 +17,7 @@ along with this program. If not, see . */ -#include +#include // For std::memset #include #include "bitboard.h" @@ -28,11 +28,13 @@ 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 TTClusterSize number of TTEntry. +/// of clusters and each cluster consists of ClusterSize number of TTEntry. void TranspositionTable::resize(size_t mbSize) { - size_t newClusterCount = size_t(1) << msb((mbSize * 1024 * 1024) / sizeof(TTCluster)); + assert(sizeof(Cluster) == CacheLineSize / 2); + + size_t newClusterCount = size_t(1) << msb((mbSize * 1024 * 1024) / sizeof(Cluster)); if (newClusterCount == clusterCount) return; @@ -40,7 +42,7 @@ void TranspositionTable::resize(size_t mbSize) { clusterCount = newClusterCount; free(mem); - mem = calloc(clusterCount * sizeof(TTCluster) + CACHE_LINE_SIZE - 1, 1); + mem = calloc(clusterCount * sizeof(Cluster) + CacheLineSize - 1, 1); if (!mem) { @@ -49,72 +51,48 @@ void TranspositionTable::resize(size_t mbSize) { exit(EXIT_FAILURE); } - table = (TTCluster*)((uintptr_t(mem) + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1)); + table = (Cluster*)((uintptr_t(mem) + CacheLineSize - 1) & ~(CacheLineSize - 1)); } /// TranspositionTable::clear() overwrites the entire transposition table -/// with zeroes. It is called whenever the table is resized, or when the +/// with zeros. It is called whenever the table is resized, or when the /// user asks the program to clear the table (from the UCI interface). void TranspositionTable::clear() { - std::memset(table, 0, clusterCount * sizeof(TTCluster)); + std::memset(table, 0, clusterCount * sizeof(Cluster)); } -/// TranspositionTable::probe() looks up the current position in the -/// transposition table. Returns a pointer to the TTEntry or NULL if -/// position is not found. +/// TranspositionTable::probe() looks up the current position in the transposition +/// table. It returns true and a pointer to the TTEntry if the position is found. +/// Otherwise, it returns false and a pointer to an empty or least valuable TTEntry +/// to be replaced later. A TTEntry t1 is considered to be more valuable than a +/// TTEntry t2 if t1 is from the current search and t2 is from a previous search, +/// or if the depth of t1 is bigger than the depth of t2. -const TTEntry* TranspositionTable::probe(const Key key) const { +TTEntry* TranspositionTable::probe(const Key key, bool& found) const { - TTEntry* tte = first_entry(key); - uint16_t key16 = key >> 48; + TTEntry* const tte = first_entry(key); + const uint16_t key16 = key >> 48; // Use the high 16 bits as key inside the cluster - for (unsigned i = 0; i < TTClusterSize; ++i, ++tte) - if (tte->key16 == key16) + for (int i = 0; i < ClusterSize; ++i) + if (!tte[i].key16 || tte[i].key16 == key16) { - tte->genBound8 = generation | tte->bound(); // Refresh - return tte; + if (tte[i].key16) + tte[i].genBound8 = uint8_t(generation8 | tte[i].bound()); // Refresh + + return found = (bool)tte[i].key16, &tte[i]; } - return NULL; -} - - -/// TranspositionTable::store() writes a new entry containing position key and -/// valuable information of current position. The lowest order bits of position -/// key are used to decide in which cluster the position will be placed. -/// When a new entry is written and there are no empty entries available in the -/// cluster, it replaces the least valuable of the entries. A TTEntry t1 is considered -/// to be more valuable than a TTEntry t2 if t1 is from the current search and t2 -/// is from a previous search, or if the depth of t1 is bigger than the depth of t2. - -void TranspositionTable::store(const Key key, Value v, Bound b, Depth d, Move m, Value statV) { - - TTEntry *tte, *replace; - 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 < TTClusterSize; ++i, ++tte) - { - if (!tte->key16 || tte->key16 == key16) // Empty or overwrite old - { - if (!m) - m = tte->move(); // Preserve any existing ttMove - - replace = tte; - break; - } - - // Implement replace strategy - if ( (( tte->genBound8 & 0xFC) == generation || tte->bound() == BOUND_EXACT) - - ((replace->genBound8 & 0xFC) == generation) - - (tte->depth8 < replace->depth8) < 0) - replace = tte; - } - - replace->save(key16, v, b, d, m, generation, statV); + // Find an entry to be replaced according to the replacement strategy + TTEntry* replace = tte; + for (int i = 1; i < ClusterSize; ++i) + if ( (( tte[i].genBound8 & 0xFC) == generation8 || tte[i].bound() == BOUND_EXACT) + - ((replace->genBound8 & 0xFC) == generation8) + - (tte[i].depth8 < replace->depth8) < 0) + replace = &tte[i]; + + return found = false, replace; } diff --git a/DroidFish/jni/stockfish/tt.h b/DroidFish/jni/stockfish/tt.h index 534409f..4df015c 100644 --- a/DroidFish/jni/stockfish/tt.h +++ b/DroidFish/jni/stockfish/tt.h @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -23,7 +23,7 @@ #include "misc.h" #include "types.h" -/// The TTEntry is the 10 bytes transposition table entry, defined as below: +/// TTEntry struct is the 10 bytes transposition table entry, defined as below: /// /// key 16 bit /// move 16 bit @@ -35,80 +35,72 @@ struct TTEntry { - Move move() const { return (Move )move16; } - Value value() const { return (Value)value16; } - Value eval_value() const { return (Value)evalValue; } - Depth depth() const { return (Depth)depth8; } - Bound bound() const { return (Bound)(genBound8 & 0x3); } + Move move() const { return (Move )move16; } + Value value() const { return (Value)value16; } + Value eval() const { return (Value)eval16; } + Depth depth() const { return (Depth)depth8; } + Bound bound() const { return (Bound)(genBound8 & 0x3); } -private: - friend class TranspositionTable; + void save(Key k, Value v, Bound b, Depth d, Move m, Value ev, uint8_t g) { - void save(uint16_t k, Value v, Bound b, Depth d, Move m, uint8_t g, Value ev) { + if (m || (k >> 48) != key16) // Preserve any existing move for the same position + move16 = (uint16_t)m; - key16 = (uint16_t)k; - move16 = (uint16_t)m; + key16 = (uint16_t)(k >> 48); value16 = (int16_t)v; - evalValue = (int16_t)ev; + eval16 = (int16_t)ev; genBound8 = (uint8_t)(g | b); depth8 = (int8_t)d; } +private: + friend class TranspositionTable; + uint16_t key16; uint16_t move16; int16_t value16; - int16_t evalValue; + int16_t eval16; 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 TTClusterSize number of TTEntry. Each non-empty entry +/// cluster consists of ClusterSize 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 int CacheLineSize = 64; + static const int ClusterSize = 3; + + struct Cluster { + TTEntry entry[ClusterSize]; + char padding[2]; // Align to the cache line size + }; + public: ~TranspositionTable() { free(mem); } - 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 new_search() { generation8 += 4; } // Lower 2 bits are used by Bound + uint8_t generation() const { return generation8; } + TTEntry* probe(const Key key, bool& found) const; void resize(size_t mbSize); void clear(); - void store(const Key key, Value v, Bound type, Depth d, Move m, Value statV); + + // The lowest order bits of the key are used to get the index of the cluster + TTEntry* first_entry(const Key key) const { + return &table[(size_t)key & (clusterCount - 1)].entry[0]; + } private: size_t clusterCount; - TTCluster* table; + Cluster* table; void* mem; - uint8_t generation; // Size must be not bigger than TTEntry::genBound8 + uint8_t generation8; // Size must be not bigger than TTEntry::genBound8 }; 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 inside the table. - -inline TTEntry* TranspositionTable::first_entry(const Key key) const { - - 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 911e580..7c5776a 100644 --- a/DroidFish/jni/stockfish/types.h +++ b/DroidFish/jni/stockfish/types.h @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -20,20 +20,19 @@ #ifndef TYPES_H_INCLUDED #define TYPES_H_INCLUDED -/// For Linux and OSX configuration is done automatically using Makefile. To get -/// started type 'make help'. +/// When compiling with provided Makefile (e.g. for Linux and OSX), configuration +/// is done automatically. To get started type 'make help'. /// -/// For Windows, part of the configuration is detected automatically, but some -/// switches need to be set manually: +/// When Makefile is not used (e.g. with Microsoft Visual Studio) some switches +/// need to be set manually: /// -/// -DNDEBUG | Disable debugging mode. Always use this. +/// -DNDEBUG | Disable debugging mode. Always use this for release. /// -/// -DNO_PREFETCH | Disable use of prefetch asm-instruction. A must if you want -/// | the executable to run on some very old machines. +/// -DNO_PREFETCH | Disable use of prefetch asm-instruction. You may need this to +/// | run on some very old machines. /// /// -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction. Works -/// | only in 64-bit mode. For compiling requires hardware with -/// | popcnt support. +/// | only in 64-bit mode and requires hardware with popcnt support. #include #include @@ -42,33 +41,33 @@ #include "platform.h" -#define unlikely(x) (x) // For code annotation purposes +/// Predefined macros hell: +/// +/// __GNUC__ Compiler is gcc, Clang or Intel on Linux +/// __INTEL_COMPILER Compiler is Intel +/// _MSC_VER Compiler is MSVC or Intel on Windows +/// _WIN32 Building on Windows (any) +/// _WIN64 Building on Windows 64 bit -#if defined(_WIN64) && !defined(IS_64BIT) +#if defined(_WIN64) && !defined(IS_64BIT) // Last condition means Makefile is not used # include // MSVC popcnt and bsfq instrinsics # define IS_64BIT # define USE_BSFQ #endif -#if defined(USE_POPCNT) && defined(_MSC_VER) && defined(__INTEL_COMPILER) +#if defined(USE_POPCNT) && defined(__INTEL_COMPILER) && defined(_MSC_VER) # include // Intel header for _mm_popcnt_u64() intrinsic #endif +#if !defined(NO_PREFETCH) && (defined(__INTEL_COMPILER) || defined(_MSC_VER)) +# include // Intel and Microsoft header for _mm_prefetch() +#endif + #if defined(USE_PEXT) # include // Header for _pext_u64() intrinsic +# define pext(b, m) _pext_u64(b, m) #else -//# define _pext_u64(b, m) (0) -#endif - -# if !defined(NO_PREFETCH) && (defined(__INTEL_COMPILER) || defined(_MSC_VER)) -# include // Intel and Microsoft header for _mm_prefetch() -# endif - -#define CACHE_LINE_SIZE 64 -#if defined(_MSC_VER) || defined(__INTEL_COMPILER) -# define CACHE_LINE_ALIGNMENT __declspec(align(CACHE_LINE_SIZE)) -#else -# define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(CACHE_LINE_SIZE))) +# define pext(b, m) (0) #endif #ifdef _MSC_VER @@ -100,9 +99,8 @@ const bool Is64Bit = false; typedef uint64_t Key; typedef uint64_t Bitboard; -const int MAX_MOVES = 256; -const int MAX_PLY = 120; -const int MAX_PLY_PLUS_6 = MAX_PLY + 6; +const int MAX_MOVES = 256; +const int MAX_PLY = 128; /// A move needs 16 bits to be stored /// @@ -139,9 +137,9 @@ enum CastlingSide { enum CastlingRight { NO_CASTLING, WHITE_OO, - WHITE_OOO = WHITE_OO << 1, - BLACK_OO = WHITE_OO << 2, - BLACK_OOO = WHITE_OO << 3, + WHITE_OOO = WHITE_OO << 1, + BLACK_OO = WHITE_OO << 2, + BLACK_OOO = WHITE_OO << 3, ANY_CASTLING = WHITE_OO | WHITE_OOO | BLACK_OO | BLACK_OOO, CASTLING_RIGHT_NB = 16 }; @@ -218,7 +216,8 @@ enum Depth { DEPTH_QS_NO_CHECKS = -1, DEPTH_QS_RECAPTURES = -5, - DEPTH_NONE = -6 + DEPTH_NONE = -6, + DEPTH_MAX = MAX_PLY }; enum Square { @@ -256,9 +255,9 @@ enum Rank { }; -/// The Score enum stores a middlegame and an endgame value in a single integer -/// (enum). The least significant 16 bits are used to store the endgame value -/// and the upper 16 bits are used to store the middlegame value. The compiler +/// Score enum stores a middlegame and an endgame value in a single integer. +/// The least significant 16 bits are used to store the endgame value and +/// the upper 16 bits are used to store the middlegame value. The compiler /// is free to choose the enum type as long as it can store the data, so we /// ensure that Score is an integer type by assigning some big int values. enum Score { @@ -267,36 +266,41 @@ enum Score { SCORE_ENSURE_INTEGER_SIZE_N = INT_MIN }; -inline Score make_score(int mg, int eg) { return Score((mg << 16) + eg); } +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) { - return Value(((s + 0x8000) & ~0xffff) / 0x10000); + + union { uint16_t u; int16_t s; } mg = { uint16_t(unsigned(s + 0x8000) >> 16) }; + return Value(mg.s); } inline Value eg_value(Score s) { - return Value((int)(unsigned(s) & 0x7FFFU) - (int)(unsigned(s) & 0x8000U)); + + union { uint16_t u; int16_t s; } eg = { uint16_t(unsigned(s)) }; + return Value(eg.s); } -#define ENABLE_BASE_OPERATORS_ON(T) \ -inline T operator+(const T d1, const T d2) { return T(int(d1) + int(d2)); } \ -inline T operator-(const T d1, const T d2) { return T(int(d1) - int(d2)); } \ -inline T operator*(int i, const T d) { return T(i * int(d)); } \ -inline T operator*(const T d, int i) { return T(int(d) * i); } \ -inline T operator-(const T d) { return T(-int(d)); } \ -inline T& operator+=(T& d1, const T d2) { return d1 = d1 + d2; } \ -inline T& operator-=(T& d1, const T d2) { return d1 = d1 - d2; } \ +#define ENABLE_BASE_OPERATORS_ON(T) \ +inline T operator+(T d1, T d2) { return T(int(d1) + int(d2)); } \ +inline T operator-(T d1, T d2) { return T(int(d1) - int(d2)); } \ +inline T operator*(int i, T d) { return T(i * int(d)); } \ +inline T operator*(T d, int i) { return T(int(d) * i); } \ +inline T operator-(T d) { return T(-int(d)); } \ +inline T& operator+=(T& d1, T d2) { return d1 = d1 + d2; } \ +inline T& operator-=(T& d1, T d2) { return d1 = d1 - d2; } \ inline T& operator*=(T& d, int i) { return d = T(int(d) * i); } -ENABLE_BASE_OPERATORS_ON(Score) - -#define ENABLE_FULL_OPERATORS_ON(T) \ -ENABLE_BASE_OPERATORS_ON(T) \ -inline T& operator++(T& d) { return d = T(int(d) + 1); } \ -inline T& operator--(T& d) { return d = T(int(d) - 1); } \ -inline T operator/(const T d, int i) { return T(int(d) / i); } \ +#define ENABLE_FULL_OPERATORS_ON(T) \ +ENABLE_BASE_OPERATORS_ON(T) \ +inline T& operator++(T& d) { return d = T(int(d) + 1); } \ +inline T& operator--(T& d) { return d = T(int(d) - 1); } \ +inline T operator/(T d, int i) { return T(int(d) / i); } \ +inline int operator/(T d1, T d2) { return int(d1) / int(d2); } \ inline T& operator/=(T& d, int i) { return d = T(int(d) / i); } ENABLE_FULL_OPERATORS_ON(Value) @@ -308,6 +312,8 @@ ENABLE_FULL_OPERATORS_ON(Square) ENABLE_FULL_OPERATORS_ON(File) ENABLE_FULL_OPERATORS_ON(Rank) +ENABLE_BASE_OPERATORS_ON(Score) + #undef ENABLE_FULL_OPERATORS_ON #undef ENABLE_BASE_OPERATORS_ON @@ -326,19 +332,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 { - Move move; - Value value; -}; - -inline bool operator<(const ExtMove& f, const ExtMove& s) { - return f.value < s.value; -} - inline Color operator~(Color c) { return Color(c ^ BLACK); } @@ -435,7 +430,7 @@ inline Move make(Square from, Square to, PieceType pt = KNIGHT) { } inline bool is_ok(Move m) { - return from_sq(m) != to_sq(m); // Catches also MOVE_NULL and MOVE_NONE + return from_sq(m) != to_sq(m); // Catch MOVE_NULL and MOVE_NONE } #endif // #ifndef TYPES_H_INCLUDED diff --git a/DroidFish/jni/stockfish/uci.cpp b/DroidFish/jni/stockfish/uci.cpp index 6027295..1b91b45 100644 --- a/DroidFish/jni/stockfish/uci.cpp +++ b/DroidFish/jni/stockfish/uci.cpp @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -17,18 +17,17 @@ along with this program. If not, see . */ -#include #include #include #include #include "evaluate.h" -#include "notation.h" +#include "movegen.h" #include "position.h" #include "search.h" #include "thread.h" #include "tt.h" -#include "ucioption.h" +#include "uci.h" using namespace std; @@ -39,9 +38,9 @@ namespace { // FEN string of the initial position, normal chess const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; - // Keep a track of the position keys along the setup moves (from the start position - // to the position just before the search starts). This is needed by the repetition - // draw detection code. + // Stack to keep track of the position states along the setup moves (from the + // start position to the position just before the search starts). Needed by + // 'draw by repetition' detection. Search::StateStackPtr SetupStates; @@ -72,7 +71,7 @@ namespace { SetupStates = Search::StateStackPtr(new std::stack()); // Parse move list (if any) - while (is >> token && (m = move_from_uci(pos, token)) != MOVE_NONE) + while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE) { SetupStates->push(StateInfo()); pos.do_move(m, SetupStates->top()); @@ -105,7 +104,7 @@ namespace { // go() is called when engine receives the "go" UCI command. The function sets - // the thinking time and other parameters from the input string, and starts + // the thinking time and other parameters from the input string, then starts // the search. void go(const Position& pos, istringstream& is) { @@ -114,10 +113,9 @@ namespace { string token; while (is >> token) - { if (token == "searchmoves") while (is >> token) - limits.searchmoves.push_back(move_from_uci(pos, token)); + limits.searchmoves.push_back(UCI::to_move(pos, token)); else if (token == "wtime") is >> limits.time[WHITE]; else if (token == "btime") is >> limits.time[BLACK]; @@ -130,7 +128,6 @@ namespace { else if (token == "mate") is >> limits.mate; else if (token == "infinite") limits.infinite = true; else if (token == "ponder") limits.ponder = true; - } Threads.start_thinking(pos, limits, SetupStates); } @@ -138,10 +135,11 @@ namespace { } // namespace -/// Wait for a command from the user, parse this text string as an UCI command, -/// and call the appropriate functions. Also intercepts EOF from stdin to ensure -/// that we exit gracefully if the GUI dies unexpectedly. In addition to the UCI -/// commands, the function also supports a few debug commands. +/// UCI::loop() waits for a command from stdin, parses it and calls the appropriate +/// function. Also intercepts EOF from stdin to ensure gracefully exiting if the +/// GUI dies unexpectedly. When called with some command line arguments, e.g. to +/// run 'bench', once the command is executed the function returns immediately. +/// In addition to the UCI ones, also some additional debug commands are supported. void UCI::loop(int argc, char* argv[]) { @@ -152,28 +150,45 @@ void UCI::loop(int argc, char* argv[]) { cmd += std::string(argv[i]) + " "; do { - if (argc == 1 && !getline(cin, cmd)) // Block here waiting for input + if (argc == 1 && !getline(cin, cmd)) // Block here waiting for input or EOF cmd = "quit"; istringstream is(cmd); + token.clear(); // getline() could return empty or blank line is >> skipws >> token; - if (token == "quit" || token == "stop" || token == "ponderhit") + // The GUI sends 'ponderhit' to tell us to ponder on the same move the + // opponent has played. In case Signals.stopOnPonderhit is set we are + // waiting for 'ponderhit' to stop the search (for instance because we + // already ran out of time), otherwise we should continue searching but + // switching from pondering to normal search. + if ( token == "quit" + || token == "stop" + || (token == "ponderhit" && Search::Signals.stopOnPonderhit)) { - // The GUI sends 'ponderhit' to tell us to ponder on the same move the - // opponent has played. In case Signals.stopOnPonderhit is set we are - // waiting for 'ponderhit' to stop the search (for instance because we - // already ran out of time), otherwise we should continue searching but - // switch from pondering to normal search. - if (token != "ponderhit" || Search::Signals.stopOnPonderhit) - { - Search::Signals.stop = true; - Threads.main()->notify_one(); // Could be sleeping - } - else - Search::Limits.ponder = false; + Search::Signals.stop = true; + Threads.main()->notify_one(); // Could be sleeping } + else if (token == "ponderhit") + Search::Limits.ponder = false; // Switch to normal search + + else if (token == "uci") + sync_cout << "id name " << engine_info(true) + << "\n" << Options + << "\nuciok" << sync_endl; + + else if (token == "isready") sync_cout << "readyok" << sync_endl; + 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); + + // Additional custom non-UCI commands, useful for debugging + else if (token == "flip") pos.flip(); + else if (token == "bench") benchmark(pos, is); + else if (token == "d") sync_cout << pos << sync_endl; + else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl; else if (token == "perft") { int depth; @@ -181,31 +196,10 @@ void UCI::loop(int argc, char* argv[]) { is >> depth; ss << Options["Hash"] << " " - << Options["Threads"] << " " << depth << " current " << token; + << Options["Threads"] << " " << depth << " current perft"; benchmark(pos, ss); } - else if (token == "key") - sync_cout << hex << uppercase << setfill('0') - << "position key: " << setw(16) << pos.key() - << "\nmaterial key: " << setw(16) << pos.material_key() - << "\npawn key: " << setw(16) << pos.pawn_key() - << dec << nouppercase << setfill(' ') << sync_endl; - - else if (token == "uci") - sync_cout << "id name " << engine_info(true) - << "\n" << Options - << "\nuciok" << sync_endl; - - 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); - else if (token == "flip") pos.flip(); - else if (token == "bench") benchmark(pos, is); - else if (token == "d") sync_cout << pos.pretty() << sync_endl; - else if (token == "isready") sync_cout << "readyok" << sync_endl; - else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl; else sync_cout << "Unknown command: " << cmd << sync_endl; @@ -213,3 +207,76 @@ void UCI::loop(int argc, char* argv[]) { Threads.wait_for_think_finished(); // Cannot quit whilst the search is running } + + +/// UCI::value() converts a Value to a string suitable for use with the UCI +/// protocol specification: +/// +/// cp The score from the engine's point of view in centipawns. +/// mate Mate in y moves, not plies. If the engine is getting mated +/// use negative values for y. + +string UCI::value(Value v) { + + stringstream ss; + + 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; + + return ss.str(); +} + + +/// UCI::square() converts a Square to a string in algebraic notation (g1, a7, etc.) + +std::string UCI::square(Square s) { + + char sq[] = { char('a' + file_of(s)), char('1' + rank_of(s)), 0 }; // NULL terminated + return sq; +} + + +/// UCI::move() converts a Move to a string in coordinate notation (g1f3, a7a8q). +/// The only special case is castling, where we print in the e1g1 notation in +/// normal chess mode, and in e1h1 notation in chess960 mode. Internally all +/// castling moves are always encoded as 'king captures rook'. + +string UCI::move(Move m, bool chess960) { + + Square from = from_sq(m); + Square to = to_sq(m); + + if (m == MOVE_NONE) + return "(none)"; + + if (m == MOVE_NULL) + return "0000"; + + if (type_of(m) == CASTLING && !chess960) + to = make_square(to > from ? FILE_G : FILE_C, rank_of(from)); + + string move = UCI::square(from) + UCI::square(to); + + if (type_of(m) == PROMOTION) + move += " pnbrqk"[promotion_type(m)]; + + return move; +} + + +/// UCI::to_move() converts a string representing a move in coordinate notation +/// (g1f3, a7a8q) to the corresponding legal Move, if any. + +Move UCI::to_move(const Position& pos, string& str) { + + if (str.length() == 5) // Junior could send promotion piece in uppercase + str[4] = char(tolower(str[4])); + + for (MoveList it(pos); *it; ++it) + if (str == UCI::move(*it, pos.is_chess960())) + return *it; + + return MOVE_NONE; +} diff --git a/DroidFish/jni/stockfish/ucioption.h b/DroidFish/jni/stockfish/uci.h similarity index 84% rename from DroidFish/jni/stockfish/ucioption.h rename to DroidFish/jni/stockfish/uci.h index 87411a4..b928e8a 100644 --- a/DroidFish/jni/stockfish/ucioption.h +++ b/DroidFish/jni/stockfish/uci.h @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -23,6 +23,10 @@ #include #include +#include "types.h" + +class Position; + namespace UCI { class Option; @@ -46,8 +50,8 @@ public: Option(const char* v, OnChange = NULL); Option(int v, int min, int max, OnChange = NULL); - Option& operator=(const std::string& v); - void operator<<(const Option& o); + Option& operator=(const std::string&); + void operator<<(const Option&); operator int() const; operator std::string() const; @@ -62,6 +66,10 @@ private: void init(OptionsMap&); void loop(int argc, char* argv[]); +std::string value(Value v); +std::string square(Square s); +std::string move(Move m, bool chess960); +Move to_move(const Position& pos, std::string& str); } // namespace UCI diff --git a/DroidFish/jni/stockfish/ucioption.cpp b/DroidFish/jni/stockfish/ucioption.cpp index 6ebf8d7..1778c71 100644 --- a/DroidFish/jni/stockfish/ucioption.cpp +++ b/DroidFish/jni/stockfish/ucioption.cpp @@ -1,7 +1,7 @@ /* 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 + Copyright (C) 2008-2015 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 @@ -22,12 +22,11 @@ #include #include -#include "evaluate.h" #include "misc.h" #include "thread.h" #include "tt.h" -#include "ucioption.h" -#include "tbprobe.h" +#include "uci.h" +#include "syzygy/tbprobe.h" using std::string; @@ -36,11 +35,10 @@ UCI::OptionsMap Options; // Global object namespace UCI { /// 'On change' actions, triggered by an option's value change -void on_logger(const Option& o) { start_logger(o); } -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_hash_size(const Option& o) { TT.resize(o); } +void on_logger(const Option& o) { start_logger(o); } +void on_threads(const Option&) { Threads.read_uci_options(); } void on_tb_path(const Option& o) { Tablebases::init(o); } @@ -56,11 +54,13 @@ bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const void init(OptionsMap& o) { + const int MaxHashMB = Is64Bit ? 1024 * 1024 : 2048; + 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["Hash"] << Option(16, 1, MaxHashMB, on_hash_size); o["Clear Hash"] << Option(on_clear_hash); o["Ponder"] << Option(true); o["MultiPV"] << Option(1, 1, 500);