From 34b9fd139f588f35bc75dbe72c372d06b05ec3ea Mon Sep 17 00:00:00 2001 From: Peter Osterlund Date: Fri, 3 May 2013 17:03:42 +0000 Subject: [PATCH] DroidFish: Updated stockfish engine to version 3. --- DroidFish/jni/stockfish/benchmark.cpp | 17 +- DroidFish/jni/stockfish/bitbase.cpp | 231 ++--- DroidFish/jni/stockfish/bitboard.cpp | 64 +- DroidFish/jni/stockfish/bitboard.h | 67 +- DroidFish/jni/stockfish/bitcount.h | 53 +- DroidFish/jni/stockfish/book.cpp | 67 +- DroidFish/jni/stockfish/book.h | 6 +- DroidFish/jni/stockfish/endgame.cpp | 100 +- DroidFish/jni/stockfish/endgame.h | 6 +- DroidFish/jni/stockfish/evaluate.cpp | 126 ++- DroidFish/jni/stockfish/evaluate.h | 4 +- DroidFish/jni/stockfish/history.h | 72 -- DroidFish/jni/stockfish/main.cpp | 2 +- DroidFish/jni/stockfish/material.cpp | 122 ++- DroidFish/jni/stockfish/material.h | 85 +- DroidFish/jni/stockfish/misc.cpp | 29 +- DroidFish/jni/stockfish/misc.h | 2 +- DroidFish/jni/stockfish/movegen.cpp | 68 +- DroidFish/jni/stockfish/movegen.h | 6 +- DroidFish/jni/stockfish/movepick.cpp | 103 +- DroidFish/jni/stockfish/movepick.h | 46 +- DroidFish/jni/stockfish/notation.cpp | 50 +- DroidFish/jni/stockfish/notation.h | 2 +- DroidFish/jni/stockfish/pawns.cpp | 249 ++--- DroidFish/jni/stockfish/pawns.h | 99 +- DroidFish/jni/stockfish/platform.h | 8 +- DroidFish/jni/stockfish/position.cpp | 548 +++++------ DroidFish/jni/stockfish/position.h | 74 +- DroidFish/jni/stockfish/psqtab.h | 4 +- DroidFish/jni/stockfish/rkiss.h | 10 +- DroidFish/jni/stockfish/search.cpp | 1307 +++++++++++++------------ DroidFish/jni/stockfish/search.h | 17 +- DroidFish/jni/stockfish/thread.cpp | 335 +++---- DroidFish/jni/stockfish/thread.h | 103 +- DroidFish/jni/stockfish/timeman.cpp | 4 +- DroidFish/jni/stockfish/timeman.h | 2 +- DroidFish/jni/stockfish/tt.cpp | 72 +- DroidFish/jni/stockfish/tt.h | 72 +- DroidFish/jni/stockfish/types.h | 82 +- DroidFish/jni/stockfish/uci.cpp | 150 ++- DroidFish/jni/stockfish/ucioption.cpp | 9 +- DroidFish/jni/stockfish/ucioption.h | 2 +- 42 files changed, 2092 insertions(+), 2383 deletions(-) delete mode 100644 DroidFish/jni/stockfish/history.h diff --git a/DroidFish/jni/stockfish/benchmark.cpp b/DroidFish/jni/stockfish/benchmark.cpp index f63cb8e..25dab6c 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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 @@ -66,7 +66,7 @@ void benchmark(const Position& current, istream& is) { vector fens; // Assign default values to missing arguments - string ttSize = (is >> token) ? token : "128"; + string ttSize = (is >> token) ? token : "32"; string threads = (is >> token) ? token : "1"; string limit = (is >> token) ? token : "12"; string fenFile = (is >> token) ? token : "default"; @@ -82,6 +82,9 @@ void benchmark(const Position& current, istream& is) { else if (limitType == "nodes") limits.nodes = atoi(limit.c_str()); + else if (limitType == "mate") + limits.mate = atoi(limit.c_str()); + else limits.depth = atoi(limit.c_str()); @@ -89,7 +92,7 @@ void benchmark(const Position& current, istream& is) { fens.assign(Defaults, Defaults + 16); else if (fenFile == "current") - fens.push_back(current.to_fen()); + fens.push_back(current.fen()); else { @@ -99,7 +102,7 @@ void benchmark(const Position& current, istream& is) { if (!file.is_open()) { cerr << "Unable to open file " << fenFile << endl; - exit(EXIT_FAILURE); + return; } while (getline(file, fen)) @@ -127,9 +130,9 @@ void benchmark(const Position& current, istream& is) { } else { - Threads.start_searching(pos, limits, vector(), st); - Threads.wait_for_search_finished(); - nodes += Search::RootPosition.nodes_searched(); + Threads.start_thinking(pos, limits, vector(), st); + Threads.wait_for_think_finished(); + nodes += Search::RootPos.nodes_searched(); } } diff --git a/DroidFish/jni/stockfish/bitbase.cpp b/DroidFish/jni/stockfish/bitbase.cpp index fdfb207..425d0f1 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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,12 +18,32 @@ */ #include +#include #include "bitboard.h" #include "types.h" namespace { + // The possible pawns squares are 24, the first 4 files and ranks from 2 to 7 + const unsigned IndexMax = 2*24*64*64; // stm * psq * wksq * bksq = 196608 + + // Each uint32_t stores results of 32 positions, one per bit + uint32_t KPKBitbase[IndexMax / 32]; + + // A KPK bitbase index is an integer in [0, IndexMax] range + // + // Information is mapped in a way that minimizes number of iterations: + // + // bit 0- 5: white king square (from SQ_A1 to SQ_H8) + // bit 6-11: black king square (from SQ_A1 to SQ_H8) + // bit 12: side to move (WHITE or BLACK) + // bit 13-14: white pawn file (from FILE_A to FILE_D) + // bit 15-17: white pawn 6 - rank (from 6 - RANK_7 to 6 - RANK_2) + unsigned index(Color us, Square bksq, Square wksq, Square psq) { + return wksq + (bksq << 6) + (us << 12) + (file_of(psq) << 13) + ((6 - rank_of(psq)) << 15); + } + enum Result { INVALID = 0, UNKNOWN = 1, @@ -35,196 +55,119 @@ namespace { struct KPKPosition { - Result classify_leaf(int idx); - Result classify(int idx, Result db[]); + operator Result() const { return res; } + Result classify_leaf(unsigned idx); + Result classify(const std::vector& db) + { return us == WHITE ? classify(db) : classify(db); } private: - template Result classify(const Result db[]) const; + template Result classify(const std::vector& db); - template Bitboard k_attacks() const { - return Us == WHITE ? StepAttacksBB[W_KING][wksq] : StepAttacksBB[B_KING][bksq]; - } - - Bitboard p_attacks() const { return StepAttacksBB[W_PAWN][psq]; } - void decode_index(int idx); - - Square wksq, bksq, psq; - Color stm; + Color us; + Square bksq, wksq, psq; + Result res; }; - // The possible pawns squares are 24, the first 4 files and ranks from 2 to 7 - const int IndexMax = 2 * 24 * 64 * 64; // stm * wp_sq * wk_sq * bk_sq = 196608 - - // Each uint32_t stores results of 32 positions, one per bit - uint32_t KPKBitbase[IndexMax / 32]; - - int index(Square wksq, Square bksq, Square psq, Color stm); -} +} // namespace -uint32_t Bitbases::probe_kpk(Square wksq, Square wpsq, Square bksq, Color stm) { +bool Bitbases::probe_kpk(Square wksq, Square wpsq, Square bksq, Color us) { - int idx = index(wksq, bksq, wpsq, stm); - return KPKBitbase[idx / 32] & (1 << (idx & 31)); + assert(file_of(wpsq) <= FILE_D); + + unsigned idx = index(us, bksq, wksq, wpsq); + return KPKBitbase[idx / 32] & (1 << (idx & 0x1F)); } void Bitbases::init_kpk() { - Result db[IndexMax]; - KPKPosition pos; - int idx, bit, repeat = 1; + unsigned idx, repeat = 1; + std::vector db(IndexMax); - // Initialize table with known win / draw positions + // Initialize db with known win / draw positions for (idx = 0; idx < IndexMax; idx++) - db[idx] = pos.classify_leaf(idx); + db[idx].classify_leaf(idx); - // Iterate until all positions are classified (30 cycles needed) + // Iterate through the positions until no more of the unknown positions can be + // changed to either wins or draws (15 cycles needed). while (repeat) for (repeat = idx = 0; idx < IndexMax; idx++) - if (db[idx] == UNKNOWN && (db[idx] = pos.classify(idx, db)) != UNKNOWN) + if (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN) repeat = 1; - // Map 32 position results into one KPKBitbase[] entry - for (idx = 0; idx < IndexMax / 32; idx++) - for (bit = 0; bit < 32; bit++) - if (db[32 * idx + bit] == WIN) - KPKBitbase[idx] |= 1 << bit; + // Map 32 results into one KPKBitbase[] entry + for (idx = 0; idx < IndexMax; idx++) + if (db[idx] == WIN) + KPKBitbase[idx / 32] |= 1 << (idx & 0x1F); } namespace { - // A KPK bitbase index is an integer in [0, IndexMax] range - // - // Information is mapped in this way - // - // bit 0: side to move (WHITE or BLACK) - // bit 1- 6: black king square (from SQ_A1 to SQ_H8) - // bit 7-12: white king square (from SQ_A1 to SQ_H8) - // bit 13-14: white pawn file (from FILE_A to FILE_D) - // bit 15-17: white pawn rank - 1 (from RANK_2 - 1 to RANK_7 - 1) + Result KPKPosition::classify_leaf(unsigned idx) { - int index(Square w, Square b, Square p, Color c) { - - assert(file_of(p) <= FILE_D); - - return c + (b << 1) + (w << 7) + (file_of(p) << 13) + ((rank_of(p) - 1) << 15); - } - - void KPKPosition::decode_index(int idx) { - - stm = Color(idx & 1); - bksq = Square((idx >> 1) & 63); - wksq = Square((idx >> 7) & 63); - psq = File((idx >> 13) & 3) | Rank((idx >> 15) + 1); - } - - Result KPKPosition::classify_leaf(int idx) { - - decode_index(idx); + wksq = Square((idx >> 0) & 0x3F); + bksq = Square((idx >> 6) & 0x3F); + us = Color((idx >> 12) & 0x01); + psq = File((idx >> 13) & 3) | Rank(6 - (idx >> 15)); // Check if two pieces are on the same square or if a king can be captured if ( wksq == psq || wksq == bksq || bksq == psq - || (k_attacks() & bksq) - || (stm == WHITE && (p_attacks() & bksq))) - return INVALID; + || (StepAttacksBB[KING][wksq] & bksq) + || (us == WHITE && (StepAttacksBB[PAWN][psq] & bksq))) + return res = INVALID; - // The position is an immediate win if it is white to move and the white - // pawn can be promoted without getting captured. - if ( rank_of(psq) == RANK_7 - && stm == WHITE - && wksq != psq + DELTA_N - && ( square_distance(bksq, psq + DELTA_N) > 1 - ||(k_attacks() & (psq + DELTA_N)))) - return WIN; + if (us == WHITE) + { + // Immediate win if pawn can be promoted without getting captured + if ( rank_of(psq) == RANK_7 + && wksq != psq + DELTA_N + && ( square_distance(bksq, psq + DELTA_N) > 1 + ||(StepAttacksBB[KING][wksq] & (psq + DELTA_N)))) + return res = WIN; + } + // Immediate draw if is stalemate or king captures undefended pawn + else if ( !(StepAttacksBB[KING][bksq] & ~(StepAttacksBB[KING][wksq] | StepAttacksBB[PAWN][psq])) + || (StepAttacksBB[KING][bksq] & psq & ~StepAttacksBB[KING][wksq])) + return res = DRAW; - // Check for known draw positions - // - // Case 1: Stalemate - if ( stm == BLACK - && !(k_attacks() & ~(k_attacks() | p_attacks()))) - return DRAW; - - // Case 2: King can capture undefended pawn - if ( stm == BLACK - && (k_attacks() & psq & ~k_attacks())) - return DRAW; - - // Case 3: Black king in front of white pawn - if ( bksq == psq + DELTA_N - && rank_of(psq) < RANK_7) - return DRAW; - - // Case 4: White king in front of pawn and black has opposition - if ( stm == WHITE - && wksq == psq + DELTA_N - && bksq == wksq + DELTA_N + DELTA_N - && rank_of(psq) < RANK_5) - return DRAW; - - // Case 5: Stalemate with rook pawn - if ( bksq == SQ_A8 - && file_of(psq) == FILE_A) - return DRAW; - - // Case 6: White king trapped on the rook file - if ( file_of(wksq) == FILE_A - && file_of(psq) == FILE_A - && rank_of(wksq) > rank_of(psq) - && bksq == wksq + 2) - return DRAW; - - return UNKNOWN; + return res = UNKNOWN; } template - Result KPKPosition::classify(const Result db[]) const { + Result KPKPosition::classify(const std::vector& db) { - // White to Move: If one move leads to a position classified as RESULT_WIN, - // the result of the current position is RESULT_WIN. If all moves lead to - // positions classified as RESULT_DRAW, the current position is classified - // RESULT_DRAW otherwise the current position is classified as RESULT_UNKNOWN. + // White to Move: If one move leads to a position classified as WIN, the result + // of the current position is WIN. If all moves lead to positions classified + // as DRAW, the current position is classified DRAW otherwise the current + // position is classified as UNKNOWN. // - // Black to Move: If one move leads to a position classified as RESULT_DRAW, - // the result of the current position is RESULT_DRAW. If all moves lead to - // positions classified as RESULT_WIN, the position is classified RESULT_WIN. - // Otherwise, the current position is classified as RESULT_UNKNOWN. + // Black to Move: If one move leads to a position classified as DRAW, the result + // of the current position is DRAW. If all moves lead to positions classified + // as WIN, the position is classified WIN otherwise the current position is + // classified UNKNOWN. Result r = INVALID; - Bitboard b = k_attacks(); + Bitboard b = StepAttacksBB[KING][Us == WHITE ? wksq : bksq]; while (b) - { - r |= Us == WHITE ? db[index(pop_lsb(&b), bksq, psq, BLACK)] - : db[index(wksq, pop_lsb(&b), psq, WHITE)]; - - if (Us == WHITE && (r & WIN)) - return WIN; - - if (Us == BLACK && (r & DRAW)) - return DRAW; - } + r |= Us == WHITE ? db[index(~Us, bksq, pop_lsb(&b), psq)] + : db[index(~Us, pop_lsb(&b), wksq, psq)]; if (Us == WHITE && rank_of(psq) < RANK_7) { Square s = psq + DELTA_N; - r |= db[index(wksq, bksq, s, BLACK)]; // Single push + r |= db[index(BLACK, bksq, wksq, s)]; // Single push if (rank_of(s) == RANK_3 && s != wksq && s != bksq) - r |= db[index(wksq, bksq, s + DELTA_N, BLACK)]; // Double push - - if (r & WIN) - return WIN; + r |= db[index(BLACK, bksq, wksq, s + DELTA_N)]; // Double push } - return r & UNKNOWN ? UNKNOWN : Us == WHITE ? DRAW : WIN; + if (Us == WHITE) + return res = r & WIN ? WIN : r & UNKNOWN ? UNKNOWN : DRAW; + else + return res = r & DRAW ? DRAW : r & UNKNOWN ? UNKNOWN : WIN; } - Result KPKPosition::classify(int idx, Result db[]) { - - decode_index(idx); - return stm == WHITE ? classify(db) : classify(db); - } - -} +} // namespace diff --git a/DroidFish/jni/stockfish/bitboard.cpp b/DroidFish/jni/stockfish/bitboard.cpp index dd5b579..a3c12ca 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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,45 +28,44 @@ CACHE_LINE_ALIGNMENT -Bitboard RMasks[64]; -Bitboard RMagics[64]; -Bitboard* RAttacks[64]; -unsigned RShifts[64]; +Bitboard RMasks[SQUARE_NB]; +Bitboard RMagics[SQUARE_NB]; +Bitboard* RAttacks[SQUARE_NB]; +unsigned RShifts[SQUARE_NB]; -Bitboard BMasks[64]; -Bitboard BMagics[64]; -Bitboard* BAttacks[64]; -unsigned BShifts[64]; +Bitboard BMasks[SQUARE_NB]; +Bitboard BMagics[SQUARE_NB]; +Bitboard* BAttacks[SQUARE_NB]; +unsigned BShifts[SQUARE_NB]; -Bitboard SquareBB[64]; -Bitboard FileBB[8]; -Bitboard RankBB[8]; -Bitboard AdjacentFilesBB[8]; -Bitboard ThisAndAdjacentFilesBB[8]; -Bitboard InFrontBB[2][8]; -Bitboard StepAttacksBB[16][64]; -Bitboard BetweenBB[64][64]; -Bitboard DistanceRingsBB[64][8]; -Bitboard ForwardBB[2][64]; -Bitboard PassedPawnMask[2][64]; -Bitboard AttackSpanMask[2][64]; -Bitboard PseudoAttacks[6][64]; +Bitboard SquareBB[SQUARE_NB]; +Bitboard FileBB[FILE_NB]; +Bitboard RankBB[RANK_NB]; +Bitboard AdjacentFilesBB[FILE_NB]; +Bitboard ThisAndAdjacentFilesBB[FILE_NB]; +Bitboard InFrontBB[COLOR_NB][RANK_NB]; +Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB]; +Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; +Bitboard DistanceRingsBB[SQUARE_NB][8]; +Bitboard ForwardBB[COLOR_NB][SQUARE_NB]; +Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB]; +Bitboard AttackSpanMask[COLOR_NB][SQUARE_NB]; +Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; -int SquareDistance[64][64]; +int SquareDistance[SQUARE_NB][SQUARE_NB]; namespace { // De Bruijn sequences. See chessprogramming.wikispaces.com/BitScan - const uint64_t DeBruijn_64 = 0x218A392CD3D5DBFULL; + const uint64_t DeBruijn_64 = 0x3F79D71B4CB0A89ULL; const uint32_t DeBruijn_32 = 0x783A9B23; CACHE_LINE_ALIGNMENT int MS1BTable[256]; - Square BSFTable[64]; + Square BSFTable[SQUARE_NB]; Bitboard RTable[0x19000]; // Storage space for rook attacks Bitboard BTable[0x1480]; // Storage space for bishop attacks - uint8_t BitCount8Bit[256]; typedef unsigned (Fn)(Square, Bitboard); @@ -75,12 +74,10 @@ namespace { FORCE_INLINE unsigned bsf_index(Bitboard b) { - if (Is64Bit) - return ((b & -b) * DeBruijn_64) >> 58; - - // Use Matt Taylor's folding trick for 32 bit systems + // Matt Taylor's folding for 32 bit systems, extended to 64 bits by Kim Walisch b ^= (b - 1); - return ((unsigned(b) ^ unsigned(b >> 32)) * DeBruijn_32) >> 26; + return Is64Bit ? (b * DeBruijn_64) >> 58 + : ((unsigned(b) ^ unsigned(b >> 32)) * DeBruijn_32) >> 26; } } @@ -161,9 +158,6 @@ void Bitboards::init() { for (int i = 0; i < 64; i++) BSFTable[bsf_index(1ULL << i)] = Square(i); - for (Bitboard b = 0; b < 256; b++) - BitCount8Bit[b] = (uint8_t)popcount(b); - for (Square s = SQ_A1; s <= SQ_H8; s++) SquareBB[s] = 1ULL << s; @@ -326,7 +320,7 @@ namespace { // until we find the one that passes the verification test. do { do magics[s] = pick_random(rk, booster); - while (BitCount8Bit[(magics[s] * masks[s]) >> 56] < 6); + while (popcount((magics[s] * masks[s]) >> 56) < 6); memset(attacks[s], 0, size * sizeof(Bitboard)); diff --git a/DroidFish/jni/stockfish/bitboard.h b/DroidFish/jni/stockfish/bitboard.h index 4c7c948..a53f348 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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 @@ -33,36 +33,37 @@ void print(Bitboard b); namespace Bitbases { void init_kpk(); -uint32_t probe_kpk(Square wksq, Square wpsq, Square bksq, Color stm); +bool probe_kpk(Square wksq, Square wpsq, Square bksq, Color us); } CACHE_LINE_ALIGNMENT -extern Bitboard RMasks[64]; -extern Bitboard RMagics[64]; -extern Bitboard* RAttacks[64]; -extern unsigned RShifts[64]; +extern Bitboard RMasks[SQUARE_NB]; +extern Bitboard RMagics[SQUARE_NB]; +extern Bitboard* RAttacks[SQUARE_NB]; +extern unsigned RShifts[SQUARE_NB]; -extern Bitboard BMasks[64]; -extern Bitboard BMagics[64]; -extern Bitboard* BAttacks[64]; -extern unsigned BShifts[64]; +extern Bitboard BMasks[SQUARE_NB]; +extern Bitboard BMagics[SQUARE_NB]; +extern Bitboard* BAttacks[SQUARE_NB]; +extern unsigned BShifts[SQUARE_NB]; -extern Bitboard SquareBB[64]; -extern Bitboard FileBB[8]; -extern Bitboard RankBB[8]; -extern Bitboard AdjacentFilesBB[8]; -extern Bitboard ThisAndAdjacentFilesBB[8]; -extern Bitboard InFrontBB[2][8]; -extern Bitboard StepAttacksBB[16][64]; -extern Bitboard BetweenBB[64][64]; -extern Bitboard DistanceRingsBB[64][8]; -extern Bitboard ForwardBB[2][64]; -extern Bitboard PassedPawnMask[2][64]; -extern Bitboard AttackSpanMask[2][64]; -extern Bitboard PseudoAttacks[6][64]; +extern Bitboard SquareBB[SQUARE_NB]; +extern Bitboard FileBB[FILE_NB]; +extern Bitboard RankBB[RANK_NB]; +extern Bitboard AdjacentFilesBB[FILE_NB]; +extern Bitboard ThisAndAdjacentFilesBB[FILE_NB]; +extern Bitboard InFrontBB[COLOR_NB][RANK_NB]; +extern Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB]; +extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; +extern Bitboard DistanceRingsBB[SQUARE_NB][8]; +extern Bitboard ForwardBB[COLOR_NB][SQUARE_NB]; +extern Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB]; +extern Bitboard AttackSpanMask[COLOR_NB][SQUARE_NB]; +extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; +const Bitboard BlackSquares = 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. @@ -199,8 +200,7 @@ inline bool squares_aligned(Square s1, Square s2, Square s3) { /// the same color of the given square. inline Bitboard same_color_squares(Square s) { - return Bitboard(0xAA55AA55AA55AA55ULL) & s ? 0xAA55AA55AA55AA55ULL - : ~0xAA55AA55AA55AA55ULL; + return BlackSquares & s ? BlackSquares : ~BlackSquares; } @@ -247,6 +247,21 @@ FORCE_INLINE Square msb(Bitboard b) { return (Square) index; } +# elif defined(__arm__) + +FORCE_INLINE int lsb32(uint32_t v) { + __asm__("rbit %0, %1" : "=r"(v) : "r"(v)); + return __builtin_clz(v); +} + +FORCE_INLINE Square msb(Bitboard b) { + return (Square) (63 - __builtin_clzll(b)); +} + +FORCE_INLINE Square lsb(Bitboard b) { + return (Square) (uint32_t(b) ? lsb32(uint32_t(b)) : 32 + lsb32(uint32_t(b >> 32))); +} + # else FORCE_INLINE Square lsb(Bitboard b) { // Assembly code by Heinz van Saanen @@ -265,7 +280,7 @@ FORCE_INLINE Square msb(Bitboard b) { FORCE_INLINE Square pop_lsb(Bitboard* b) { const Square s = lsb(*b); - *b &= ~(1ULL << s); + *b &= *b - 1; return s; } diff --git a/DroidFish/jni/stockfish/bitcount.h b/DroidFish/jni/stockfish/bitcount.h index ce2a69a..2300bc9 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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 @@ -33,8 +33,8 @@ enum BitCountType { }; /// Determine at compile time the best popcount<> specialization according if -/// platform is 32 or 64 bits, to the maximum number of nonzero bits to count or -/// use hardware popcnt instruction when available. +/// platform is 32 or 64 bits, to the maximum number of nonzero bits to count +/// and if hardware popcnt instruction is available. 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; @@ -44,44 +44,38 @@ template inline int popcount(Bitboard); template<> inline int popcount(Bitboard b) { - b -= ((b>>1) & 0x5555555555555555ULL); - b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL); - b = ((b>>4) + b) & 0x0F0F0F0F0F0F0F0FULL; - b *= 0x0101010101010101ULL; - return int(b >> 56); + b -= (b >> 1) & 0x5555555555555555ULL; + b = ((b >> 2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL); + b = ((b >> 4) + b) & 0x0F0F0F0F0F0F0F0FULL; + return (b * 0x0101010101010101ULL) >> 56; } template<> inline int popcount(Bitboard b) { - b -= (b>>1) & 0x5555555555555555ULL; - b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL); - b *= 0x1111111111111111ULL; - return int(b >> 60); + b -= (b >> 1) & 0x5555555555555555ULL; + b = ((b >> 2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL); + return (b * 0x1111111111111111ULL) >> 60; } template<> inline int popcount(Bitboard b) { unsigned w = unsigned(b >> 32), v = unsigned(b); - v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits - w -= (w >> 1) & 0x55555555; - v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits - w = ((w >> 2) & 0x33333333) + (w & 0x33333333); - v = ((v >> 4) + v) & 0x0F0F0F0F; // 0-8 in 8 bits - v += (((w >> 4) + w) & 0x0F0F0F0F); // 0-16 in 8 bits - v *= 0x01010101; // mul is fast on amd procs - return int(v >> 24); + v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits + w -= (w >> 1) & 0x55555555; + v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits + w = ((w >> 2) & 0x33333333) + (w & 0x33333333); + v = ((v >> 4) + v + (w >> 4) + w) & 0x0F0F0F0F; + return (v * 0x01010101) >> 24; } template<> inline int popcount(Bitboard b) { unsigned w = unsigned(b >> 32), v = unsigned(b); - v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits - w -= (w >> 1) & 0x55555555; - v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits - w = ((w >> 2) & 0x33333333) + (w & 0x33333333); - v += w; // 0-8 in 4 bits - v *= 0x11111111; - return int(v >> 28); + v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits + w -= (w >> 1) & 0x55555555; + v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits + w = ((w >> 2) & 0x33333333) + (w & 0x33333333); + return ((v + w) * 0x11111111) >> 28; } template<> @@ -102,9 +96,8 @@ inline int popcount(Bitboard b) { #else - unsigned long ret; - __asm__("popcnt %1, %0" : "=r" (ret) : "r" (b)); - return ret; + __asm__("popcnt %1, %0" : "=r" (b) : "r" (b)); + return b; #endif } diff --git a/DroidFish/jni/stockfish/book.cpp b/DroidFish/jni/stockfish/book.cpp index b723705..b8bce9e 100644 --- a/DroidFish/jni/stockfish/book.cpp +++ b/DroidFish/jni/stockfish/book.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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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 @@ -38,7 +38,7 @@ namespace { // A Polyglot book is a series of "entries" of 16 bytes. All integers are // stored in big-endian format, with highest byte first (regardless of size). // The entries are ordered according to the key in ascending order. - struct BookEntry { + struct Entry { uint64_t key; uint16_t move; uint16_t count; @@ -46,7 +46,15 @@ namespace { }; // Random numbers from PolyGlot, used to compute book hash keys - const Key PolyGlotRandoms[781] = { + const union { + Key PolyGlotRandoms[781]; + struct { + Key psq[12][64]; // [piece][square] + Key castle[4]; // [castle right] + Key enpassant[8]; // [file] + Key turn; + } Zobrist; + } PG = {{ 0x9D39247E33776D41ULL, 0x2AF7398005AAA5C7ULL, 0x44DB015024623547ULL, 0x9C15F73E62A76AE2ULL, 0x75834465489C0C89ULL, 0x3290AC3A203001BFULL, 0x0FBBAD1F61042279ULL, 0xE83A908FF2FB60CAULL, 0x0D7E765D58755C10ULL, @@ -308,51 +316,40 @@ namespace { 0x003A93D8B2806962ULL, 0x1C99DED33CB890A1ULL, 0xCF3145DE0ADD4289ULL, 0xD0E4427A5514FB72ULL, 0x77C621CC9FB3A483ULL, 0x67A34DAC4356550BULL, 0xF8D626AAAF278509ULL - }; + }}; - // Offsets to the PolyGlotRandoms[] array of zobrist keys - const Key* ZobPiece = PolyGlotRandoms; - const Key* ZobCastle = ZobPiece + 12 * 64; // Pieces * squares - const Key* ZobEnPassant = ZobCastle + 4; // Castle flags - const Key* ZobTurn = ZobEnPassant + 8; // Number of files + // polyglot_key() returns the PolyGlot hash key of the given position + Key polyglot_key(const Position& pos) { - // book_key() returns the PolyGlot hash key of the given position - uint64_t book_key(const Position& pos) { - - uint64_t key = 0; + Key key = 0; Bitboard b = pos.pieces(); while (b) { - // In PolyGlotRandoms[] pieces are stored in the following sequence: - // BP = 0, WP = 1, BN = 2, WN = 3, ... BK = 10, WK = 11 Square s = pop_lsb(&b); Piece p = pos.piece_on(s); - int pieceOfs = 2 * (type_of(p) - 1) + (color_of(p) == WHITE); - key ^= ZobPiece[64 * pieceOfs + s]; + + // PolyGlot pieces are: BP = 0, WP = 1, BN = 2, ... BK = 10, WK = 11 + key ^= PG.Zobrist.psq[2 * (type_of(p) - 1) + (color_of(p) == WHITE)][s]; } b = pos.can_castle(ALL_CASTLES); while (b) - key ^= ZobCastle[pop_lsb(&b)]; + key ^= PG.Zobrist.castle[pop_lsb(&b)]; if (pos.ep_square() != SQ_NONE) - key ^= ZobEnPassant[file_of(pos.ep_square())]; + key ^= PG.Zobrist.enpassant[file_of(pos.ep_square())]; if (pos.side_to_move() == WHITE) - key ^= ZobTurn[0]; + key ^= PG.Zobrist.turn; return key; } } // namespace -PolyglotBook::PolyglotBook() { - - for (int i = Time::now() % 10000; i > 0; i--) - RKiss.rand(); // Make random number generation less deterministic -} +PolyglotBook::PolyglotBook() : rkiss(Time::now() % 10000) {} PolyglotBook::~PolyglotBook() { if (is_open()) close(); } @@ -370,7 +367,7 @@ template PolyglotBook& PolyglotBook::operator>>(T& n) { return *this; } -template<> PolyglotBook& PolyglotBook::operator>>(BookEntry& e) { +template<> PolyglotBook& PolyglotBook::operator>>(Entry& e) { return *this >> e.key >> e.move >> e.count >> e.learn; } @@ -400,13 +397,13 @@ Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest if (fileName != fName && !open(fName.c_str())) return MOVE_NONE; - BookEntry e; + Entry e; uint16_t best = 0; unsigned sum = 0; Move move = MOVE_NONE; - uint64_t key = book_key(pos); + Key key = polyglot_key(pos); - seekg(find_first(key) * sizeof(BookEntry), ios_base::beg); + seekg(find_first(key) * sizeof(Entry), ios_base::beg); while (*this >> e, e.key == key && good()) { @@ -416,7 +413,7 @@ Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest // Choose book move according to its score. If a move has a very // high score it has higher probability to be choosen than a move // with lower score. Note that first entry is always chosen. - if ( (sum && RKiss.rand() % sum < e.count) + if ( (sum && rkiss.rand() % sum < e.count) || (pickBest && e.count == best)) move = Move(e.move); } @@ -440,7 +437,7 @@ Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest // Add 'special move' flags and verify it is legal for (MoveList ml(pos); !ml.end(); ++ml) - if (move == (ml.move() & 0x3FFF)) + if (move == (ml.move() ^ type_of(ml.move()))) return ml.move(); return MOVE_NONE; @@ -451,12 +448,12 @@ Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest /// the book file for the given key. Returns the index of the leftmost book /// entry with the same key as the input. -size_t PolyglotBook::find_first(uint64_t key) { +size_t PolyglotBook::find_first(Key key) { seekg(0, ios::end); // Move pointer to end, so tellg() gets file's size - size_t low = 0, mid, high = (size_t)tellg() / sizeof(BookEntry) - 1; - BookEntry e; + size_t low = 0, mid, high = (size_t)tellg() / sizeof(Entry) - 1; + Entry e; assert(low <= high); @@ -466,7 +463,7 @@ size_t PolyglotBook::find_first(uint64_t key) { assert(mid >= low && mid < high); - seekg(mid * sizeof(BookEntry), ios_base::beg); + seekg(mid * sizeof(Entry), ios_base::beg); *this >> e; if (key <= e.key) diff --git a/DroidFish/jni/stockfish/book.h b/DroidFish/jni/stockfish/book.h index fcbce65..ed44663 100644 --- a/DroidFish/jni/stockfish/book.h +++ b/DroidFish/jni/stockfish/book.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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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 @@ -36,9 +36,9 @@ private: template PolyglotBook& operator>>(T& n); bool open(const char* fName); - size_t find_first(uint64_t key); + size_t find_first(Key key); - RKISS RKiss; + RKISS rkiss; std::string fileName; }; diff --git a/DroidFish/jni/stockfish/endgame.cpp b/DroidFish/jni/stockfish/endgame.cpp index 20d175b..52786cd 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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 @@ -31,7 +31,7 @@ namespace { // Table used to drive the defending king towards the edge of the board // in KX vs K and KQ vs KR endgames. - const int MateTable[64] = { + const int MateTable[SQUARE_NB] = { 100, 90, 80, 70, 70, 80, 90, 100, 90, 70, 60, 50, 50, 60, 70, 90, 80, 60, 40, 30, 30, 40, 60, 80, @@ -44,7 +44,7 @@ namespace { // Table used to drive the defending king towards a corner square of the // right color in KBN vs K endgames. - const int KBNKMateTable[64] = { + const int KBNKMateTable[SQUARE_NB] = { 200, 190, 180, 170, 160, 150, 140, 130, 190, 180, 170, 160, 150, 140, 130, 140, 180, 170, 155, 140, 140, 125, 140, 150, @@ -95,10 +95,12 @@ Endgames::Endgames() { add("KRKP"); add("KRKB"); add("KRKN"); + add("KQKP"); add("KQKR"); add("KBBKN"); add("KNPK"); + add("KNPKB"); add("KRPKR"); add("KBPKB"); add("KBPKN"); @@ -132,7 +134,7 @@ Value Endgame::operator()(const Position& pos) const { // Stalemate detection with lone king if ( pos.side_to_move() == weakerSide - && !pos.in_check() + && !pos.checkers() && !MoveList(pos).size()) { return VALUE_DRAW; } @@ -169,12 +171,12 @@ Value Endgame::operator()(const Position& pos) const { Square winnerKSq = pos.king_square(strongerSide); Square loserKSq = pos.king_square(weakerSide); - Square bishopSquare = pos.piece_list(strongerSide, BISHOP)[0]; + Square bishopSq = pos.piece_list(strongerSide, BISHOP)[0]; // kbnk_mate_table() tries to drive toward corners A1 or H8, // if we have a bishop that cannot reach the above squares we // mirror the kings so to drive enemy toward corners A8 or H1. - if (opposite_colors(bishopSquare, SQ_A1)) + if (opposite_colors(bishopSq, SQ_A1)) { winnerKSq = mirror(winnerKSq); loserKSq = mirror(loserKSq); @@ -198,21 +200,21 @@ Value Endgame::operator()(const Position& pos) const { assert(pos.piece_count(weakerSide, PAWN) == 0); Square wksq, bksq, wpsq; - Color stm; + Color us; if (strongerSide == WHITE) { wksq = pos.king_square(WHITE); bksq = pos.king_square(BLACK); wpsq = pos.piece_list(WHITE, PAWN)[0]; - stm = pos.side_to_move(); + us = pos.side_to_move(); } else { wksq = ~pos.king_square(BLACK); bksq = ~pos.king_square(WHITE); wpsq = ~pos.piece_list(BLACK, PAWN)[0]; - stm = ~pos.side_to_move(); + us = ~pos.side_to_move(); } if (file_of(wpsq) >= FILE_E) @@ -222,7 +224,7 @@ Value Endgame::operator()(const Position& pos) const { wpsq = mirror(wpsq); } - if (!Bitbases::probe_kpk(wksq, wpsq, bksq, stm)) + if (!Bitbases::probe_kpk(wksq, wpsq, bksq, us)) return VALUE_DRAW; Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(wpsq)); @@ -326,6 +328,37 @@ Value Endgame::operator()(const Position& pos) const { } +/// KQ vs KP. In general, a win for the stronger side, however, there are a few +/// important exceptions. Pawn on 7th rank, A,C,F or H file, with king next can +/// be a draw, so we scale down to distance between kings only. +template<> +Value Endgame::operator()(const Position& pos) const { + + assert(pos.non_pawn_material(strongerSide) == QueenValueMg); + assert(pos.piece_count(strongerSide, PAWN) == 0); + assert(pos.non_pawn_material(weakerSide) == 0); + assert(pos.piece_count(weakerSide, PAWN) == 1); + + Square winnerKSq = pos.king_square(strongerSide); + Square loserKSq = pos.king_square(weakerSide); + Square pawnSq = pos.piece_list(weakerSide, PAWN)[0]; + + Value result = QueenValueEg + - PawnValueEg + + DistanceBonus[square_distance(winnerKSq, loserKSq)]; + + if ( square_distance(loserKSq, pawnSq) == 1 + && relative_rank(weakerSide, pawnSq) == RANK_7) + { + File f = file_of(pawnSq); + + if (f == FILE_A || f == FILE_C || f == FILE_F || f == FILE_H) + result = Value(DistanceBonus[square_distance(winnerKSq, loserKSq)]); + } + return strongerSide == pos.side_to_move() ? result : -result; +} + + /// KQ vs KR. This is almost identical to KX vs K: We give the attacking /// king a bonus for having the kings close together, and for forcing the /// defending king towards the edge. If we also take care to avoid null move @@ -439,6 +472,29 @@ ScaleFactor Endgame::operator()(const Position& pos) const { return SCALE_FACTOR_DRAW; } } + + // All pawns on same B or G file? Then potential draw + if ( (pawnFile == FILE_B || pawnFile == FILE_G) + && !(pos.pieces(PAWN) & ~file_bb(pawnFile)) + && pos.non_pawn_material(weakerSide) == 0 + && pos.piece_count(weakerSide, PAWN) >= 1) + { + // Get weaker pawn closest to opponent's queening square + Bitboard wkPawns = pos.pieces(weakerSide, PAWN); + Square weakerPawnSq = strongerSide == WHITE ? msb(wkPawns) : lsb(wkPawns); + + Square strongerKingSq = pos.king_square(strongerSide); + Square weakerKingSq = pos.king_square(weakerSide); + Square bishopSq = pos.piece_list(strongerSide, BISHOP)[0]; + + // Draw if weaker pawn is on rank 7, bishop can't attack the pawn, and + // weaker king can stop opposing opponent's king from penetrating. + if ( relative_rank(strongerSide, weakerPawnSq) == RANK_7 + && opposite_colors(bishopSq, weakerPawnSq) + && square_distance(weakerPawnSq, weakerKingSq) <= square_distance(weakerPawnSq, strongerKingSq)) + return SCALE_FACTOR_DRAW; + } + return SCALE_FACTOR_NONE; } @@ -849,6 +905,24 @@ ScaleFactor Endgame::operator()(const Position& pos) const { } +/// K, knight and a pawn vs K and bishop. If knight can block bishop from taking +/// pawn, it's a win. Otherwise, drawn. +template<> +ScaleFactor Endgame::operator()(const Position& pos) const { + + Square pawnSq = pos.piece_list(strongerSide, PAWN)[0]; + Square bishopSq = pos.piece_list(weakerSide, BISHOP)[0]; + Square weakerKingSq = pos.king_square(weakerSide); + + // 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(strongerSide, pawnSq) & pos.attacks_from(bishopSq)) + return ScaleFactor(square_distance(weakerKingSq, pawnSq)); + + return SCALE_FACTOR_NONE; +} + + /// K and a pawn vs K and a pawn. This is done by removing the weakest side's /// pawn and probing the KP vs K bitbase: If the weakest side has a draw without /// the pawn, she probably has at least a draw with the pawn as well. The exception @@ -865,14 +939,14 @@ ScaleFactor Endgame::operator()(const Position& pos) const { Square wksq = pos.king_square(strongerSide); Square bksq = pos.king_square(weakerSide); Square wpsq = pos.piece_list(strongerSide, PAWN)[0]; - Color stm = pos.side_to_move(); + Color us = pos.side_to_move(); if (strongerSide == BLACK) { wksq = ~wksq; bksq = ~bksq; wpsq = ~wpsq; - stm = ~stm; + us = ~us; } if (file_of(wpsq) >= FILE_E) @@ -890,5 +964,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, wpsq, bksq, stm) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW; + return Bitbases::probe_kpk(wksq, wpsq, bksq, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW; } diff --git a/DroidFish/jni/stockfish/endgame.h b/DroidFish/jni/stockfish/endgame.h index 0d7c063..b560c79 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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 @@ -39,6 +39,7 @@ enum EndgameType { KRKP, // KR vs KP KRKB, // KR vs KB KRKN, // KR vs KN + KQKP, // KQ vs KP KQKR, // KQ vs KR KBBKN, // KBB vs KN KNNK, // KNN vs K @@ -57,6 +58,7 @@ enum EndgameType { KBPPKB, // KBPP vs KB KBPKN, // KBP vs KN KNPK, // KNP vs K + KNPKB, // KNP vs KB KPKP // KP vs KP }; @@ -111,7 +113,7 @@ class Endgames { public: Endgames(); - ~Endgames(); + ~Endgames(); template T probe(Key key, T& eg) { return eg = map(eg).count(key) ? map(eg)[key] : NULL; } diff --git a/DroidFish/jni/stockfish/evaluate.cpp b/DroidFish/jni/stockfish/evaluate.cpp index b5b51d2..e861f5f 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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 @@ -36,13 +36,13 @@ namespace { struct EvalInfo { // Pointers to material and pawn hash table entries - MaterialEntry* mi; - PawnEntry* pi; + Material::Entry* mi; + Pawns::Entry* pi; // attackedBy[color][piece type] is a bitboard representing all squares - // attacked by a given color and piece type, attackedBy[color][0] contains - // all squares attacked by the given color. - Bitboard attackedBy[2][8]; + // attacked by a given color and piece type, attackedBy[color][ALL_PIECES] + // contains all squares attacked by the given color. + Bitboard attackedBy[COLOR_NB][PIECE_TYPE_NB]; // kingRing[color] is the zone around the king which is considered // by the king safety evaluation. This consists of the squares directly @@ -50,25 +50,25 @@ namespace { // squares two ranks in front of the king. For instance, if black's king // is on g8, kingRing[BLACK] is a bitboard containing the squares f8, h8, // f7, g7, h7, f6, g6 and h6. - Bitboard kingRing[2]; + Bitboard kingRing[COLOR_NB]; // kingAttackersCount[color] is the number of pieces of the given color // which attack a square in the kingRing of the enemy king. - int kingAttackersCount[2]; + int kingAttackersCount[COLOR_NB]; // 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 - int kingAttackersWeight[2]; + int kingAttackersWeight[COLOR_NB]; // kingAdjacentZoneAttacksCount[color] is the number of attacks to squares // directly adjacent to the king of the given color. Pieces which attack // more than one square are counted multiple times. For instance, if black's // king is on g8 and there's a white knight on g5, this knight adds // 2 to kingAdjacentZoneAttacksCount[BLACK]. - int kingAdjacentZoneAttacksCount[2]; + int kingAdjacentZoneAttacksCount[COLOR_NB]; }; // Evaluation grain size, must be a power of 2 @@ -88,7 +88,7 @@ namespace { // // Values modified by Joona Kiiski const Score WeightsInternal[] = { - S(252, 344), S(216, 266), S(46, 0), S(247, 0), S(259, 0) + S(289, 344), S(221, 273), S(46, 0), S(271, 0), S(307, 0) }; // MobilityBonus[PieceType][attacked] contains mobility bonuses for middle and @@ -114,7 +114,7 @@ namespace { // OutpostBonus[PieceType][Square] contains outpost bonuses of knights and // bishops, indexed by piece type and square (from white's point of view). - const Value OutpostBonus[][64] = { + const Value OutpostBonus[][SQUARE_NB] = { { // A B C D E F G H V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0), // Knights @@ -134,7 +134,7 @@ namespace { // ThreatBonus[attacking][attacked] contains threat bonuses according to // which piece type attacks which one. - const Score ThreatBonus[][8] = { + const Score ThreatBonus[][PIECE_TYPE_NB] = { {}, {}, { S(0, 0), S( 7, 39), S( 0, 0), S(24, 49), S(41,100), S(41,100) }, // KNIGHT { S(0, 0), S( 7, 39), S(24, 49), S( 0, 0), S(41,100), S(41,100) }, // BISHOP @@ -150,16 +150,18 @@ namespace { #undef S + const Score BishopPinBonus = make_score(66, 11); + // Bonus for having the side to move (modified by Joona Kiiski) const Score Tempo = make_score(24, 11); // Rooks and queens on the 7th rank - const Score RookOn7thBonus = make_score(3, 20); - const Score QueenOn7thBonus = make_score(1, 8); + const Score RookOn7thBonus = make_score(11, 20); + const Score QueenOn7thBonus = make_score( 3, 8); // Rooks and queens attacking pawns on the same rank - const Score RookOnPawnBonus = make_score(3, 48); - const Score QueenOnPawnBonus = make_score(1, 40); + const Score RookOnPawnBonus = make_score(10, 28); + const Score QueenOnPawnBonus = make_score( 4, 20); // Rooks on open files (modified by Joona Kiiski) const Score RookOpenFileBonus = make_score(43, 21); @@ -169,6 +171,9 @@ namespace { // right to castle. const Value TrappedRookPenalty = Value(180); + // Penalty for bishop with pawns on the same coloured squares + const Score BishopPawnsPenalty = make_score(8, 12); + // Penalty for a bishop on a1/h1 (a8/h8 for black) which is trapped by // a friendly pawn on b2/g2 (b7/g7 for black). This can obviously only // happen in Chess960 games. @@ -221,11 +226,11 @@ namespace { // KingDangerTable[Color][attackUnits] contains the actual king danger // weighted scores, indexed by color and by a calculated integer number. - Score KingDangerTable[2][128]; + Score KingDangerTable[COLOR_NB][128]; // TracedTerms[Color][PieceType || TracedType] contains a breakdown of the // evaluation terms, used when tracing. - Score TracedScores[2][16]; + Score TracedScores[COLOR_NB][16]; std::stringstream TraceStream; enum TracedType { @@ -267,8 +272,6 @@ namespace { namespace Eval { - Color RootColor; - /// evaluate() is the main evaluation function. It always computes two /// values, an endgame score and a middle game score, and interpolates /// between them based on the remaining material. @@ -289,14 +292,6 @@ namespace Eval { Weights[KingDangerUs] = weight_option("Cowardice", "Cowardice", WeightsInternal[KingDangerUs]); Weights[KingDangerThem] = weight_option("Aggressiveness", "Aggressiveness", WeightsInternal[KingDangerThem]); - // King safety is asymmetrical. Our king danger level is weighted by - // "Cowardice" UCI parameter, instead the opponent one by "Aggressiveness". - // If running in analysis mode, make sure we use symmetrical king safety. We - // do this by replacing both Weights[kingDangerUs] and Weights[kingDangerThem] - // by their average. - if (Options["UCI_AnalyseMode"]) - Weights[KingDangerUs] = Weights[KingDangerThem] = (Weights[KingDangerUs] + Weights[KingDangerThem]) / 2; - const int MaxSlope = 30; const int Peak = 1280; @@ -319,7 +314,7 @@ namespace Eval { Value margin; std::string totals; - RootColor = pos.side_to_move(); + Search::RootColor = pos.side_to_move(); TraceStream.str(""); TraceStream << std::showpoint << std::showpos << std::fixed << std::setprecision(2); @@ -363,11 +358,12 @@ namespace { template Value do_evaluate(const Position& pos, Value& margin) { - assert(!pos.in_check()); + assert(!pos.checkers()); EvalInfo ei; - Value margins[2]; + Value margins[COLOR_NB]; Score score, mobilityWhite, mobilityBlack; + Thread* th = pos.this_thread(); // margins[] store the uncertainty estimation of position's evaluation // that typically is used by the search for pruning decisions. @@ -379,7 +375,7 @@ Value do_evaluate(const Position& pos, Value& margin) { score = pos.psq_score() + (pos.side_to_move() == WHITE ? Tempo : -Tempo); // Probe the material hash table - ei.mi = pos.this_thread()->materialTable.probe(pos); + ei.mi = Material::probe(pos, th->materialTable, th->endgames); score += ei.mi->material_value(); // If we have a specialized evaluation function for the current material @@ -391,7 +387,7 @@ Value do_evaluate(const Position& pos, Value& margin) { } // Probe the pawn hash table - ei.pi = pos.this_thread()->pawnTable.probe(pos); + ei.pi = Pawns::probe(pos, th->pawnsTable); score += ei.pi->pawns_value(); // Initialize attack and king safety bitboards @@ -496,7 +492,7 @@ Value do_evaluate(const Position& pos, Value& margin) { // Init king safety tables only if we are going to use them if ( pos.piece_count(Us, QUEEN) - && pos.non_pawn_material(Us) >= QueenValueMg + RookValueMg) + && pos.non_pawn_material(Us) > QueenValueMg + PawnValueMg) { ei.kingRing[Them] = (b | (Us == WHITE ? b >> 8 : b << 8)); b &= ei.attackedBy[Us][PAWN]; @@ -577,23 +573,22 @@ Value do_evaluate(const Position& pos, Value& margin) { mobility += MobilityBonus[Piece][mob]; - // Add a bonus if a slider is pinning an enemy piece - if ( (Piece == BISHOP || Piece == ROOK || Piece == QUEEN) - && (PseudoAttacks[Piece][pos.king_square(Them)] & s)) - { - b = BetweenBB[s][pos.king_square(Them)] & pos.pieces(); - - assert(b); - - if (!more_than_one(b) && (b & pos.pieces(Them))) - score += ThreatBonus[Piece][type_of(pos.piece_on(lsb(b)))]; - } - // Decrease score if we are attacked by an enemy pawn. Remaining part // of threat evaluation must be done later when we have full attack info. if (ei.attackedBy[Them][PAWN] & s) score -= ThreatenedByPawnPenalty[Piece]; + // Otherwise give a bonus if we are a bishop and can pin a piece or + // can give a discovered check through an x-ray attack. + else if ( Piece == BISHOP + && (PseudoAttacks[Piece][pos.king_square(Them)] & s) + && !more_than_one(BetweenBB[s][pos.king_square(Them)] & pos.pieces())) + score += BishopPinBonus; + + // Penalty for bishop with same coloured pawns + if (Piece == BISHOP) + score -= BishopPawnsPenalty * ei.pi->pawns_on_same_color_squares(Us, s); + // Bishop and knight outposts squares if ( (Piece == BISHOP || Piece == KNIGHT) && !(pos.pieces(Them, PAWN) & attack_span_mask(Us, s))) @@ -696,16 +691,15 @@ Value do_evaluate(const Position& pos, Value& margin) { // Undefended minors get penalized even if not under attack undefendedMinors = pos.pieces(Them) & (pos.pieces(BISHOP) | pos.pieces(KNIGHT)) - & ~ei.attackedBy[Them][0]; + & ~ei.attackedBy[Them][ALL_PIECES]; if (undefendedMinors) - score += more_than_one(undefendedMinors) ? UndefendedMinorPenalty * 2 - : UndefendedMinorPenalty; + score += UndefendedMinorPenalty; // Enemy pieces not defended by a pawn and under our attack weakEnemies = pos.pieces(Them) & ~ei.attackedBy[Them][PAWN] - & ei.attackedBy[Us][0]; + & ei.attackedBy[Us][ALL_PIECES]; if (!weakEnemies) return score; @@ -744,9 +738,9 @@ Value do_evaluate(const Position& pos, Value& margin) { score += evaluate_pieces(pos, ei, mobility, mobilityArea); // Sum up all attacked squares - ei.attackedBy[Us][0] = ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT] - | ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK] - | ei.attackedBy[Us][QUEEN] | ei.attackedBy[Us][KING]; + ei.attackedBy[Us][ALL_PIECES] = ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT] + | ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK] + | ei.attackedBy[Us][QUEEN] | ei.attackedBy[Us][KING]; return score; } @@ -772,7 +766,7 @@ Value do_evaluate(const Position& pos, Value& margin) { { // Find the attacked squares around the king which has no defenders // apart from the king itself - undefended = ei.attackedBy[Them][0] & ei.attackedBy[Us][KING]; + undefended = ei.attackedBy[Them][ALL_PIECES] & ei.attackedBy[Us][KING]; undefended &= ~( ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT] | ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK] | ei.attackedBy[Us][QUEEN]); @@ -820,7 +814,7 @@ Value do_evaluate(const Position& pos, Value& margin) { } // Analyse enemy's safe distance checks for sliders and knights - safe = ~(pos.pieces(Them) | ei.attackedBy[Us][0]); + safe = ~(pos.pieces(Them) | ei.attackedBy[Us][ALL_PIECES]); b1 = pos.attacks_from(ksq) & safe; b2 = pos.attacks_from(ksq) & safe; @@ -853,8 +847,8 @@ Value do_evaluate(const Position& pos, Value& margin) { // value that will be used for pruning because this value can sometimes // be very big, and so capturing a single attacking piece can therefore // result in a score change far bigger than the value of the captured piece. - score -= KingDangerTable[Us == Eval::RootColor][attackUnits]; - margins[Us] += mg_value(KingDangerTable[Us == Eval::RootColor][attackUnits]); + score -= KingDangerTable[Us == Search::RootColor][attackUnits]; + margins[Us] += mg_value(KingDangerTable[Us == Search::RootColor][attackUnits]); } if (Trace) @@ -907,7 +901,7 @@ Value do_evaluate(const Position& pos, Value& margin) { if (pos.is_empty(blockSq)) { squaresToQueen = forward_bb(Us, s); - defendedSquares = squaresToQueen & ei.attackedBy[Us][0]; + defendedSquares = squaresToQueen & ei.attackedBy[Us][ALL_PIECES]; // If there is an enemy rook or queen attacking the pawn from behind, // add all X-ray attacks by the rook or queen. Otherwise consider only @@ -916,7 +910,7 @@ Value do_evaluate(const Position& pos, Value& margin) { && (forward_bb(Them, s) & pos.pieces(Them, ROOK, QUEEN) & pos.attacks_from(s))) unsafeSquares = squaresToQueen; else - unsafeSquares = squaresToQueen & (ei.attackedBy[Them][0] | pos.pieces(Them)); + unsafeSquares = squaresToQueen & (ei.attackedBy[Them][ALL_PIECES] | pos.pieces(Them)); // If there aren't enemy attacks or pieces along the path to queen give // huge bonus. Even bigger if we protect the pawn's path. @@ -993,14 +987,14 @@ Value do_evaluate(const Position& pos, Value& margin) { // Compute plies to queening and check direct advancement movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(c, s) == RANK_2); oppMovesToGo = square_distance(pos.king_square(~c), queeningSquare) - int(c != pos.side_to_move()); - pathDefended = ((ei.attackedBy[c][0] & queeningPath) == queeningPath); + pathDefended = ((ei.attackedBy[c][ALL_PIECES] & queeningPath) == queeningPath); if (movesToGo >= oppMovesToGo && !pathDefended) continue; // Opponent king cannot block because path is defended and position // is not in check. So only friendly pieces can be blockers. - assert(!pos.in_check()); + assert(!pos.checkers()); assert((queeningPath & pos.pieces()) == (queeningPath & pos.pieces(c))); // Add moves needed to free the path from friendly pieces and retest condition @@ -1140,14 +1134,18 @@ Value do_evaluate(const Position& pos, Value& margin) { Bitboard safe = SpaceMask[Us] & ~pos.pieces(Us, PAWN) & ~ei.attackedBy[Them][PAWN] - & (ei.attackedBy[Us][0] | ~ei.attackedBy[Them][0]); + & (ei.attackedBy[Us][ALL_PIECES] | ~ei.attackedBy[Them][ALL_PIECES]); // Find all squares which are at most three squares behind some friendly pawn Bitboard behind = pos.pieces(Us, PAWN); behind |= (Us == WHITE ? behind >> 8 : behind << 8); behind |= (Us == WHITE ? behind >> 16 : behind << 16); - return popcount(safe) + popcount(behind & safe); + // Since SpaceMask[Us] is fully on our half of the board + 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)); } diff --git a/DroidFish/jni/stockfish/evaluate.h b/DroidFish/jni/stockfish/evaluate.h index a0418b7..8eb3964 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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 @@ -26,8 +26,6 @@ class Position; namespace Eval { -extern Color RootColor; - extern void init(); extern Value evaluate(const Position& pos, Value& margin); extern std::string trace(const Position& pos); diff --git a/DroidFish/jni/stockfish/history.h b/DroidFish/jni/stockfish/history.h deleted file mode 100644 index 7899c84..0000000 --- a/DroidFish/jni/stockfish/history.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2012 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 . -*/ - -#if !defined(HISTORY_H_INCLUDED) -#define HISTORY_H_INCLUDED - -#include -#include - -#include "types.h" - -/// The History class stores statistics about how often different moves -/// have been successful or unsuccessful during the current search. These -/// statistics are used for reduction and move ordering decisions. History -/// entries are stored according only to moving piece and destination square, -/// in particular two moves with different origin but same destination and -/// same piece will be considered identical. - -class History { - -public: - void clear(); - Value value(Piece p, Square to) const; - void add(Piece p, Square to, Value bonus); - Value gain(Piece p, Square to) const; - void update_gain(Piece p, Square to, Value g); - - static const Value MaxValue = Value(2000); - -private: - Value history[16][64]; // [piece][to_square] - Value maxGains[16][64]; // [piece][to_square] -}; - -inline void History::clear() { - memset(history, 0, 16 * 64 * sizeof(Value)); - memset(maxGains, 0, 16 * 64 * sizeof(Value)); -} - -inline Value History::value(Piece p, Square to) const { - return history[p][to]; -} - -inline void History::add(Piece p, Square to, Value bonus) { - if (abs(history[p][to] + bonus) < MaxValue) history[p][to] += bonus; -} - -inline Value History::gain(Piece p, Square to) const { - return maxGains[p][to]; -} - -inline void History::update_gain(Piece p, Square to, Value g) { - maxGains[p][to] = std::max(g, maxGains[p][to] - 1); -} - -#endif // !defined(HISTORY_H_INCLUDED) diff --git a/DroidFish/jni/stockfish/main.cpp b/DroidFish/jni/stockfish/main.cpp index e4ebcc3..2a04308 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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/material.cpp b/DroidFish/jni/stockfish/material.cpp index 3236117..208227e 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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::min #include #include @@ -38,15 +38,29 @@ namespace { const Value RedundantQueenPenalty = Value(320); const Value RedundantRookPenalty = Value(554); - const int LinearCoefficients[6] = { 1617, -162, -1172, -190, 105, 26 }; + // pair pawn knight bishop rook queen + const int LinearCoefficients[6] = { 1617, -162, -1172, -190, 105, 26 }; - const int QuadraticCoefficientsSameColor[][8] = { - { 7, 7, 7, 7, 7, 7 }, { 39, 2, 7, 7, 7, 7 }, { 35, 271, -4, 7, 7, 7 }, - { 7, 25, 4, 7, 7, 7 }, { -27, -2, 46, 100, 56, 7 }, { 58, 29, 83, 148, -3, -25 } }; + const int QuadraticCoefficientsSameColor[][PIECE_TYPE_NB] = { + // pair pawn knight bishop rook queen + { 7 }, // Bishop pair + { 39, 2 }, // Pawn + { 35, 271, -4 }, // Knight + { 7, 105, 4, 7 }, // Bishop + { -27, -2, 46, 100, 56 }, // Rook + { 58, 29, 83, 148, -3, -25 } // Queen + }; - const int QuadraticCoefficientsOppositeColor[][8] = { - { 41, 41, 41, 41, 41, 41 }, { 37, 41, 41, 41, 41, 41 }, { 10, 62, 41, 41, 41, 41 }, - { 57, 64, 39, 41, 41, 41 }, { 50, 40, 23, -22, 41, 41 }, { 106, 101, 3, 151, 171, 41 } }; + const int QuadraticCoefficientsOppositeColor[][PIECE_TYPE_NB] = { + // THEIR PIECES + // pair pawn knight bishop rook queen + { 41 }, // Bishop pair + { 37, 41 }, // Pawn + { 10, 62, 41 }, // Knight OUR PIECES + { 57, 64, 39, 41 }, // Bishop + { 50, 40, 23, -22, 41 }, // Rook + { 106, 101, 3, 151, 171, 41 } // Queen + }; // Endgame evaluation and scaling functions accessed direcly and not through // the function maps because correspond to more then one material hash key. @@ -81,18 +95,54 @@ namespace { && pos.piece_count(Them, PAWN) >= 1; } + /// imbalance() calculates imbalance comparing piece count of each + /// piece type for both colors. + + template + int imbalance(const int pieceCount[][PIECE_TYPE_NB]) { + + const Color Them = (Us == WHITE ? BLACK : WHITE); + + int pt1, pt2, pc, v; + int value = 0; + + // Redundancy of major pieces, formula based on Kaufman's paper + // "The Evaluation of Material Imbalances in Chess" + if (pieceCount[Us][ROOK] > 0) + value -= RedundantRookPenalty * (pieceCount[Us][ROOK] - 1) + + RedundantQueenPenalty * pieceCount[Us][QUEEN]; + + // Second-degree polynomial material imbalance by Tord Romstad + for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; pt1++) + { + pc = pieceCount[Us][pt1]; + if (!pc) + continue; + + v = LinearCoefficients[pt1]; + + for (pt2 = NO_PIECE_TYPE; pt2 <= pt1; pt2++) + v += QuadraticCoefficientsSameColor[pt1][pt2] * pieceCount[Us][pt2] + + QuadraticCoefficientsOppositeColor[pt1][pt2] * pieceCount[Them][pt2]; + + value += pc * v; + } + return value; + } + } // namespace +namespace Material { -/// MaterialTable::probe() takes a position object as input, looks up a MaterialEntry +/// 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. -MaterialEntry* MaterialTable::probe(const Position& pos) { +Entry* probe(const Position& pos, Table& entries, Endgames& endgames) { Key key = pos.material_key(); - MaterialEntry* e = entries[key]; + Entry* e = entries[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 @@ -100,10 +150,10 @@ MaterialEntry* MaterialTable::probe(const Position& pos) { if (e->key == key) return e; - memset(e, 0, sizeof(MaterialEntry)); + memset(e, 0, sizeof(Entry)); e->key = key; e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL; - e->gamePhase = MaterialTable::game_phase(pos); + e->gamePhase = game_phase(pos); // Let's look if we have a specialized evaluation function for this // particular material configuration. First we look for a fixed @@ -215,7 +265,7 @@ MaterialEntry* MaterialTable::probe(const Position& pos) { // Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder // for the bishop pair "extended piece", this allow us to be more flexible // in defining bishop pair bonuses. - const int pieceCount[2][8] = { + const int pieceCount[COLOR_NB][PIECE_TYPE_NB] = { { pos.piece_count(WHITE, BISHOP) > 1, pos.piece_count(WHITE, PAWN), pos.piece_count(WHITE, KNIGHT), pos.piece_count(WHITE, BISHOP) , pos.piece_count(WHITE, ROOK), pos.piece_count(WHITE, QUEEN) }, { pos.piece_count(BLACK, BISHOP) > 1, pos.piece_count(BLACK, PAWN), pos.piece_count(BLACK, KNIGHT), @@ -226,47 +276,11 @@ MaterialEntry* MaterialTable::probe(const Position& pos) { } -/// MaterialTable::imbalance() calculates imbalance comparing piece count of each -/// piece type for both colors. - -template -int MaterialTable::imbalance(const int pieceCount[][8]) { - - const Color Them = (Us == WHITE ? BLACK : WHITE); - - int pt1, pt2, pc, v; - int value = 0; - - // Redundancy of major pieces, formula based on Kaufman's paper - // "The Evaluation of Material Imbalances in Chess" - if (pieceCount[Us][ROOK] > 0) - value -= RedundantRookPenalty * (pieceCount[Us][ROOK] - 1) - + RedundantQueenPenalty * pieceCount[Us][QUEEN]; - - // Second-degree polynomial material imbalance by Tord Romstad - for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; pt1++) - { - pc = pieceCount[Us][pt1]; - if (!pc) - continue; - - v = LinearCoefficients[pt1]; - - for (pt2 = NO_PIECE_TYPE; pt2 <= pt1; pt2++) - v += QuadraticCoefficientsSameColor[pt1][pt2] * pieceCount[Us][pt2] - + QuadraticCoefficientsOppositeColor[pt1][pt2] * pieceCount[Them][pt2]; - - value += pc * v; - } - return value; -} - - -/// MaterialTable::game_phase() calculates the phase given the current +/// Material::game_phase() calculates the phase given the current /// position. Because the phase is strictly a function of the material, it /// is stored in MaterialEntry. -Phase MaterialTable::game_phase(const Position& pos) { +Phase game_phase(const Position& pos) { Value npm = pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK); @@ -274,3 +288,5 @@ Phase MaterialTable::game_phase(const Position& pos) { : npm <= EndgameLimit ? PHASE_ENDGAME : Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit)); } + +} // namespace Material diff --git a/DroidFish/jni/stockfish/material.h b/DroidFish/jni/stockfish/material.h index 75074f9..751edec 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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,96 +25,53 @@ #include "position.h" #include "types.h" -const int MaterialTableSize = 8192; +namespace Material { -/// Game phase -enum Phase { - PHASE_ENDGAME = 0, - PHASE_MIDGAME = 128 -}; - - -/// MaterialEntry is a class which contains various information about a -/// material configuration. It contains a material balance 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" for black and white. +/// Material::Entry contains various information about a material configuration. +/// It contains a material balance 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". /// /// 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. -class MaterialEntry { +struct Entry { - friend struct MaterialTable; - -public: - Score material_value() const; + Score material_value() const { return make_score(value, value); } + int space_weight() const { return spaceWeight; } + Phase game_phase() const { return gamePhase; } + bool specialized_eval_exists() const { return evaluationFunction != NULL; } + Value evaluate(const Position& p) const { return (*evaluationFunction)(p); } ScaleFactor scale_factor(const Position& pos, Color c) const; - int space_weight() const; - Phase game_phase() const; - bool specialized_eval_exists() const; - Value evaluate(const Position& pos) const; -private: Key key; int16_t value; - uint8_t factor[2]; + uint8_t factor[COLOR_NB]; EndgameBase* evaluationFunction; - EndgameBase* scalingFunction[2]; + EndgameBase* scalingFunction[COLOR_NB]; int spaceWeight; Phase gamePhase; }; +typedef HashTable Table; -/// The MaterialTable class represents a material hash table. The most important -/// method is probe(), which returns a pointer to a MaterialEntry object. +Entry* probe(const Position& pos, Table& entries, Endgames& endgames); +Phase game_phase(const Position& pos); -struct MaterialTable { - - MaterialEntry* probe(const Position& pos); - static Phase game_phase(const Position& pos); - template static int imbalance(const int pieceCount[][8]); - - HashTable entries; - Endgames endgames; -}; - - -/// MaterialEntry::scale_factor takes a position and a color as input, and +/// Material::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 /// to 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 /// which checks for draws with rook pawns and wrong-colored bishops. -inline ScaleFactor MaterialEntry::scale_factor(const Position& pos, Color c) const { +inline ScaleFactor Entry::scale_factor(const Position& pos, Color c) const { - if (!scalingFunction[c]) - return ScaleFactor(factor[c]); - - ScaleFactor sf = (*scalingFunction[c])(pos); - return sf == SCALE_FACTOR_NONE ? ScaleFactor(factor[c]) : sf; + return !scalingFunction[c] || (*scalingFunction[c])(pos) == SCALE_FACTOR_NONE + ? ScaleFactor(factor[c]) : (*scalingFunction[c])(pos); } -inline Value MaterialEntry::evaluate(const Position& pos) const { - return (*evaluationFunction)(pos); -} - -inline Score MaterialEntry::material_value() const { - return make_score(value, value); -} - -inline int MaterialEntry::space_weight() const { - return spaceWeight; -} - -inline Phase MaterialEntry::game_phase() const { - return gamePhase; -} - -inline bool MaterialEntry::specialized_eval_exists() const { - return evaluationFunction != NULL; } #endif // !defined(MATERIAL_H_INCLUDED) diff --git a/DroidFish/jni/stockfish/misc.cpp b/DroidFish/jni/stockfish/misc.cpp index 627b5d4..78e1ce8 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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 @@ -31,16 +31,16 @@ using namespace std; /// Version number. If Version is left empty, then Tag plus current -/// date (in the format YYMMDD) is used as a version number. +/// date, in the format DD-MM-YY, are used as a version number. -static const string Version = "2.3.1"; +static const string Version = "3"; static const string Tag = ""; -/// engine_info() returns the full name of the current Stockfish version. -/// This will be either "Stockfish YYMMDD" (where YYMMDD is the date when -/// the program was compiled) or "Stockfish ", depending -/// on whether Version is empty. +/// 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) { @@ -57,8 +57,8 @@ const string engine_info(bool to_uci) { { date >> month >> day >> year; - s << Tag << setfill('0') << " " << year.substr(2) - << setw(2) << (1 + months.find(month) / 4) << setw(2) << day; + s << Tag << string(Tag.empty() ? "" : " ") << setfill('0') << setw(2) << day + << "-" << setw(2) << (1 + months.find(month) / 4) << "-" << year.substr(2); } s << cpu64 << popcnt << (to_uci ? "\nid author ": " by ") @@ -227,18 +227,19 @@ void prefetch(char*) {} #else -# include - void prefetch(char* addr) { -# if defined(__INTEL_COMPILER) || defined(__ICL) +# if defined(__INTEL_COMPILER) // This hack prevents prefetches to be optimized away by // Intel compiler. Both MSVC and gcc seems not affected. __asm__ (""); # endif - _mm_prefetch(addr, _MM_HINT_T2); - _mm_prefetch(addr+64, _MM_HINT_T2); // 64 bytes ahead +# if defined(__INTEL_COMPILER) || defined(_MSC_VER) + _mm_prefetch(addr, _MM_HINT_T0); +# else + __builtin_prefetch(addr); +# endif } #endif diff --git a/DroidFish/jni/stockfish/misc.h b/DroidFish/jni/stockfish/misc.h index 9650f06..c09d2c6 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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/movegen.cpp b/DroidFish/jni/stockfish/movegen.cpp index be848cf..63c1e1f 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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 @@ -44,7 +44,7 @@ namespace { Square kto = relative_square(us, Side == KING_SIDE ? SQ_G1 : SQ_C1); Bitboard enemies = pos.pieces(~us); - assert(!pos.in_check()); + assert(!pos.checkers()); const int K = Chess960 ? kto > kfrom ? -1 : 1 : Side == KING_SIDE ? -1 : 1; @@ -249,41 +249,38 @@ namespace { } - FORCE_INLINE MoveStack* generate_king_moves(const Position& pos, MoveStack* mlist, - Color us, Bitboard target) { - Square from = pos.king_square(us); - Bitboard b = pos.attacks_from(from) & target; - SERIALIZE(b); - return mlist; - } - - template FORCE_INLINE - MoveStack* generate_all_moves(const Position& pos, MoveStack* mlist, Color us, - Bitboard target, const CheckInfo* ci = NULL) { + MoveStack* generate_all(const Position& pos, MoveStack* mlist, Color us, + Bitboard target, const CheckInfo* ci = NULL) { + + const bool Checks = Type == QUIET_CHECKS; mlist = (us == WHITE ? generate_pawn_moves(pos, mlist, target, ci) : 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(pos, mlist, us, target, ci); - mlist = generate_moves(pos, mlist, us, target, ci); + mlist = generate_moves(pos, mlist, us, target, ci); + mlist = generate_moves(pos, mlist, us, target, ci); + mlist = generate_moves(pos, mlist, us, target, ci); + mlist = generate_moves(pos, mlist, us, target, ci); if (Type != QUIET_CHECKS && Type != EVASIONS) - mlist = generate_king_moves(pos, mlist, us, target); + { + Square from = pos.king_square(us); + Bitboard b = pos.attacks_from(from) & target; + SERIALIZE(b); + } if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(us)) { if (pos.is_chess960()) { - mlist = generate_castle(pos, mlist, us); - mlist = generate_castle(pos, mlist, us); + mlist = generate_castle(pos, mlist, us); + mlist = generate_castle(pos, mlist, us); } else { - mlist = generate_castle(pos, mlist, us); - mlist = generate_castle(pos, mlist, us); + mlist = generate_castle(pos, mlist, us); + mlist = generate_castle(pos, mlist, us); } } @@ -307,21 +304,15 @@ template MoveStack* generate(const Position& pos, MoveStack* mlist) { assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS); - assert(!pos.in_check()); + assert(!pos.checkers()); Color us = pos.side_to_move(); - Bitboard target; - if (Type == CAPTURES) - target = pos.pieces(~us); + Bitboard target = Type == CAPTURES ? pos.pieces(~us) + : Type == QUIETS ? ~pos.pieces() + : Type == NON_EVASIONS ? ~pos.pieces(us) : 0; - else if (Type == QUIETS) - target = ~pos.pieces(); - - else if (Type == NON_EVASIONS) - target = ~pos.pieces(us); - - return generate_all_moves(pos, mlist, us, target); + return generate_all(pos, mlist, us, target); } // Explicit template instantiations @@ -335,9 +326,8 @@ template MoveStack* generate(const Position&, MoveStack*); template<> MoveStack* generate(const Position& pos, MoveStack* mlist) { - assert(!pos.in_check()); + assert(!pos.checkers()); - Color us = pos.side_to_move(); CheckInfo ci(pos); Bitboard dc = ci.dcCandidates; @@ -357,7 +347,7 @@ MoveStack* generate(const Position& pos, MoveStack* mlist) { SERIALIZE(b); } - return generate_all_moves(pos, mlist, us, ~pos.pieces(), &ci); + return generate_all(pos, mlist, pos.side_to_move(), ~pos.pieces(), &ci); } @@ -366,7 +356,7 @@ MoveStack* generate(const Position& pos, MoveStack* mlist) { template<> MoveStack* generate(const Position& pos, MoveStack* mlist) { - assert(pos.in_check()); + assert(pos.checkers()); Square from, checksq; int checkersCnt = 0; @@ -419,7 +409,7 @@ MoveStack* generate(const Position& pos, MoveStack* mlist) { // Generate blocking evasions or captures of the checking piece Bitboard target = between_bb(checksq, ksq) | pos.checkers(); - return generate_all_moves(pos, mlist, us, target); + return generate_all(pos, mlist, us, target); } @@ -432,7 +422,7 @@ MoveStack* generate(const Position& pos, MoveStack* mlist) { Bitboard pinned = pos.pinned_pieces(); Square ksq = pos.king_square(pos.side_to_move()); - end = pos.in_check() ? generate(pos, mlist) + end = pos.checkers() ? generate(pos, mlist) : generate(pos, mlist); while (cur != end) if ( (pinned || from_sq(cur->move) == ksq || type_of(cur->move) == ENPASSANT) diff --git a/DroidFish/jni/stockfish/movegen.h b/DroidFish/jni/stockfish/movegen.h index 84d5d35..c96e73c 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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 @@ -46,6 +46,10 @@ struct MoveList { bool end() const { return cur == last; } Move move() const { return cur->move; } size_t size() const { return last - mlist; } + bool contains(Move m) const { + for (const MoveStack* it(mlist); it != last; ++it) if (it->move == m) return true; + return false; + } private: MoveStack mlist[MAX_MOVES]; diff --git a/DroidFish/jni/stockfish/movepick.cpp b/DroidFish/jni/stockfish/movepick.cpp index 89577ee..984520a 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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,10 +18,8 @@ along with this program. If not, see . */ -#include #include -#include "movegen.h" #include "movepick.h" #include "thread.h" @@ -37,6 +35,20 @@ namespace { STOP }; + // Our insertion sort, guaranteed to be stable, as is needed + void insertion_sort(MoveStack* begin, MoveStack* end) + { + MoveStack tmp, *p, *q; + + for (p = begin + 1; p < end; ++p) + { + tmp = *p; + for (q = p; q != begin && *(q-1) < tmp; --q) + *q = *(q-1); + *q = tmp; + } + } + // Unary predicate used by std::partition to split positive scores from remaining // ones so to sort separately the two sets, and with the second sort delayed. inline bool has_positive_score(const MoveStack& ms) { return ms.score > 0; } @@ -59,7 +71,7 @@ namespace { /// move ordering is at the current node. MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h, - Search::Stack* s, Value beta) : pos(p), H(h), depth(d) { + Search::Stack* s, Value beta) : pos(p), Hist(h), depth(d) { assert(d > DEPTH_ZERO); @@ -68,7 +80,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h, endBadCaptures = moves + MAX_MOVES - 1; ss = s; - if (p.in_check()) + if (p.checkers()) phase = EVASION; else @@ -79,12 +91,12 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h, killers[1].move = ss->killers[1]; // Consider sligtly negative captures as good if at low depth and far from beta - if (ss && ss->eval < beta - PawnValueMg && d < 3 * ONE_PLY) + if (ss && ss->staticEval < beta - PawnValueMg && d < 3 * ONE_PLY) captureThreshold = -PawnValueMg; // Consider negative captures as good if still enough to reach beta - else if (ss && ss->eval > beta) - captureThreshold = beta - ss->eval; + else if (ss && ss->staticEval > beta) + captureThreshold = beta - ss->staticEval; } ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE); @@ -92,11 +104,11 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h, } MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h, - Square sq) : pos(p), H(h), cur(moves), end(moves) { + Square sq) : pos(p), Hist(h), cur(moves), end(moves) { assert(d <= DEPTH_ZERO); - if (p.in_check()) + if (p.checkers()) phase = EVASION; else if (d > DEPTH_QS_NO_CHECKS) @@ -124,14 +136,14 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h, } MovePicker::MovePicker(const Position& p, Move ttm, const History& h, PieceType pt) - : pos(p), H(h), cur(moves), end(moves) { + : pos(p), Hist(h), cur(moves), end(moves) { - assert(!pos.in_check()); + assert(!pos.checkers()); phase = PROBCUT; // In ProbCut we generate only captures better than parent's captured piece - captureThreshold = PieceValue[Mg][pt]; + captureThreshold = PieceValue[MG][pt]; ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE); if (ttMove && (!pos.is_capture(ttMove) || pos.see(ttMove) <= captureThreshold)) @@ -141,12 +153,10 @@ MovePicker::MovePicker(const Position& p, Move ttm, const History& h, PieceType } -/// MovePicker::score_captures(), MovePicker::score_noncaptures() and -/// MovePicker::score_evasions() assign a numerical move ordering score -/// to each move in a move list. The moves with highest scores will be -/// picked first by next_move(). - -void MovePicker::score_captures() { +/// score() assign a numerical move ordering score to each move in a move list. +/// The moves with highest scores will be picked first. +template<> +void MovePicker::score() { // Winning and equal captures in the main search are ordered by MVV/LVA. // Suprisingly, this appears to perform slightly better than SEE based // move ordering. The reason is probably that in a position with a winning @@ -165,51 +175,54 @@ void MovePicker::score_captures() { for (MoveStack* it = moves; it != end; ++it) { m = it->move; - it->score = PieceValue[Mg][pos.piece_on(to_sq(m))] + it->score = PieceValue[MG][pos.piece_on(to_sq(m))] - type_of(pos.piece_moved(m)); if (type_of(m) == PROMOTION) - it->score += PieceValue[Mg][promotion_type(m)]; + it->score += PieceValue[MG][promotion_type(m)] - PieceValue[MG][PAWN]; + + else if (type_of(m) == ENPASSANT) + it->score += PieceValue[MG][PAWN]; } } -void MovePicker::score_noncaptures() { +template<> +void MovePicker::score() { Move m; for (MoveStack* it = moves; it != end; ++it) { m = it->move; - it->score = H.value(pos.piece_moved(m), to_sq(m)); + it->score = Hist[pos.piece_moved(m)][to_sq(m)]; } } -void MovePicker::score_evasions() { +template<> +void MovePicker::score() { // Try good captures ordered by MVV/LVA, then non-captures if destination square // is not under attack, ordered by history value, then bad-captures and quiet // moves with a negative SEE. This last group is ordered by the SEE score. Move m; int seeScore; - if (end < moves + 2) - return; - for (MoveStack* it = moves; it != end; ++it) { m = it->move; if ((seeScore = pos.see_sign(m)) < 0) - it->score = seeScore - History::MaxValue; // Be sure we are at the bottom + it->score = seeScore - History::Max; // At the bottom + else if (pos.is_capture(m)) - it->score = PieceValue[Mg][pos.piece_on(to_sq(m))] - - type_of(pos.piece_moved(m)) + History::MaxValue; + it->score = PieceValue[MG][pos.piece_on(to_sq(m))] + - type_of(pos.piece_moved(m)) + History::Max; else - it->score = H.value(pos.piece_moved(m), to_sq(m)); + it->score = Hist[pos.piece_moved(m)][to_sq(m)]; } } -/// MovePicker::generate_next() generates, scores and sorts the next bunch of moves, -/// when there are no more moves to try for the current phase. +/// generate_next() generates, scores and sorts the next bunch of moves, when +/// there are no more moves to try for the current phase. void MovePicker::generate_next() { @@ -219,7 +232,7 @@ void MovePicker::generate_next() { case CAPTURES_S1: case CAPTURES_S3: case CAPTURES_S4: case CAPTURES_S5: case CAPTURES_S6: end = generate(pos, moves); - score_captures(); + score(); return; case KILLERS_S1: @@ -229,16 +242,16 @@ void MovePicker::generate_next() { case QUIETS_1_S1: endQuiets = end = generate(pos, moves); - score_noncaptures(); + score(); end = std::partition(cur, end, has_positive_score); - sort(cur, end); + insertion_sort(cur, end); return; case QUIETS_2_S1: cur = end; end = endQuiets; if (depth >= 3 * ONE_PLY) - sort(cur, end); + insertion_sort(cur, end); return; case BAD_CAPTURES_S1: @@ -249,7 +262,8 @@ void MovePicker::generate_next() { case EVASIONS_S2: end = generate(pos, moves); - score_evasions(); + if (end > moves + 1) + score(); return; case QUIET_CHECKS_S3: @@ -268,11 +282,10 @@ void MovePicker::generate_next() { } -/// MovePicker::next_move() is the most important method of the MovePicker class. -/// It returns a new pseudo legal move every time it is called, until there -/// are no more moves left. It picks the move with the biggest score from a list -/// of generated moves taking care not to return the tt move if has already been -/// searched previously. +/// next_move() is the most important method of the MovePicker class. It returns +/// a new pseudo legal move every time is called, until there are no more moves +/// left. It picks the move with the biggest score from a list of generated moves +/// taking care not returning the ttMove if has already been searched previously. template<> Move MovePicker::next_move() { @@ -359,6 +372,6 @@ Move MovePicker::next_move() { /// Version of next_move() to use at split point nodes where the move is grabbed /// from the split point's shared MovePicker object. This function is not thread -/// safe so should be lock protected by the caller. +/// safe so must be lock protected by the caller. template<> -Move MovePicker::next_move() { return ss->sp->mp->next_move(); } +Move MovePicker::next_move() { return ss->splitPoint->movePicker->next_move(); } diff --git a/DroidFish/jni/stockfish/movepick.h b/DroidFish/jni/stockfish/movepick.h index 9459b8c..3e472fb 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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,12 +20,48 @@ #if !defined MOVEPICK_H_INCLUDED #define MOVEPICK_H_INCLUDED -#include "history.h" +#include // For std::max +#include // For memset + +#include "movegen.h" #include "position.h" #include "search.h" #include "types.h" +/// The Stats struct stores moves statistics. According to the template parameter +/// the class can store both History and Gains type statistics. History records +/// how often different moves have been successful or unsuccessful during the +/// current search and is used for reduction and move ordering decisions. Gains +/// records the move's best evaluation gain from one ply to the next and is used +/// for pruning decisions. Entries are stored according only to moving piece and +/// destination square, in particular two moves with different origin but same +/// destination and same piece will be considered identical. +template +struct Stats { + + static const Value Max = Value(2000); + + const Value* operator[](Piece p) const { return &table[p][0]; } + void clear() { memset(table, 0, sizeof(table)); } + + void update(Piece p, Square to, Value v) { + + if (Gain) + table[p][to] = std::max(v, table[p][to] - 1); + + else if (abs(table[p][to] + v) < Max) + table[p][to] += v; + } + +private: + Value table[PIECE_NB][SQUARE_NB]; +}; + +typedef Stats History; +typedef Stats Gains; + + /// MovePicker class is used to pick one pseudo legal move at a time from the /// current position. The most important method is next_move(), which returns a /// new pseudo legal move each time it is called, until there are no moves left, @@ -44,13 +80,11 @@ public: template Move next_move(); private: - void score_captures(); - void score_noncaptures(); - void score_evasions(); + template void score(); void generate_next(); const Position& pos; - const History& H; + const History& Hist; Search::Stack* ss; Depth depth; Move ttMove; diff --git a/DroidFish/jni/stockfish/notation.cpp b/DroidFish/jni/stockfish/notation.cpp index fb65f6f..2b4174e 100644 --- a/DroidFish/jni/stockfish/notation.cpp +++ b/DroidFish/jni/stockfish/notation.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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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 @@ #include #include #include -#include +#include #include "movegen.h" #include "notation.h" @@ -28,7 +28,7 @@ using namespace std; -static const char* PieceToChar = " PNBRQK pnbrqk"; +static const char* PieceToChar[COLOR_NB] = { " PNBRQK", " pnbrqk" }; /// score_to_uci() converts a value to a string suitable for use with the UCI @@ -75,7 +75,7 @@ const string move_to_uci(Move m, bool chess960) { string move = square_to_string(from) + square_to_string(to); if (type_of(m) == PROMOTION) - move += PieceToChar[make_piece(BLACK, promotion_type(m))]; // Lower case + move += PieceToChar[BLACK][promotion_type(m)]; // Lower case return move; } @@ -108,10 +108,9 @@ const string move_to_san(Position& pos, Move m) { if (m == MOVE_NULL) return "(null)"; - assert(pos.move_is_legal(m)); + assert(MoveList(pos).contains(m)); - Bitboard attackers; - bool ambiguousMove, ambiguousFile, ambiguousRank; + Bitboard others, b; string san; Color us = pos.side_to_move(); Square from = from_sq(m); @@ -125,33 +124,25 @@ const string move_to_san(Position& pos, Move m) { { if (pt != PAWN) { - san = PieceToChar[pt]; // Upper case + san = PieceToChar[WHITE][pt]; // Upper case - // Disambiguation if we have more then one piece with destination 'to' - // note that for pawns is not needed because starting file is explicit. - ambiguousMove = ambiguousFile = ambiguousRank = false; + // Disambiguation if we have more then one piece of type 'pt' that can + // reach 'to' with a legal move. + others = b = (pos.attacks_from(pc, to) & pos.pieces(us, pt)) ^ from; - attackers = (pos.attacks_from(pc, to) & pos.pieces(us, pt)) ^ from; - - while (attackers) + while (b) { - Square sq = pop_lsb(&attackers); - - // Pinned pieces are not included in the possible sub-set - if (!pos.pl_move_is_legal(make_move(sq, to), pos.pinned_pieces())) - continue; - - ambiguousFile |= file_of(sq) == file_of(from); - ambiguousRank |= rank_of(sq) == rank_of(from); - ambiguousMove = true; + Move move = make_move(pop_lsb(&b), to); + if (!pos.pl_move_is_legal(move, pos.pinned_pieces())) + others ^= from_sq(move); } - if (ambiguousMove) + if (others) { - if (!ambiguousFile) + if (!(others & file_bb(from))) san += file_to_char(file_of(from)); - else if (!ambiguousRank) + else if (!(others & rank_bb(from))) san += rank_to_char(rank_of(from)); else @@ -167,7 +158,7 @@ const string move_to_san(Position& pos, Move m) { san += square_to_string(to); if (type_of(m) == PROMOTION) - san += string("=") + PieceToChar[promotion_type(m)]; + san += string("=") + PieceToChar[WHITE][promotion_type(m)]; } if (pos.move_gives_check(m, CheckInfo(pos))) @@ -226,7 +217,7 @@ string pretty_pv(Position& pos, int depth, Value value, int64_t msecs, Move pv[] const int64_t K = 1000; const int64_t M = 1000000; - StateInfo state[MAX_PLY_PLUS_2], *st = state; + std::stack st; Move* m = pv; string san, padding; size_t length; @@ -261,7 +252,8 @@ string pretty_pv(Position& pos, int depth, Value value, int64_t msecs, Move pv[] s << san << ' '; length += san.length() + 1; - pos.do_move(*m++, *st++); + st.push(StateInfo()); + pos.do_move(*m++, st.top()); } while (m != pv) diff --git a/DroidFish/jni/stockfish/notation.h b/DroidFish/jni/stockfish/notation.h index 0c5f2fe..d757bd4 100644 --- a/DroidFish/jni/stockfish/notation.h +++ b/DroidFish/jni/stockfish/notation.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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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/pawns.cpp b/DroidFish/jni/stockfish/pawns.cpp index 069c8d9..5b64ac6 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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 @@ -30,34 +30,34 @@ namespace { #define S(mg, eg) make_score(mg, eg) // Doubled pawn penalty by opposed flag and file - const Score DoubledPawnPenalty[2][8] = { + const Score DoubledPawnPenalty[2][FILE_NB] = { { S(13, 43), S(20, 48), S(23, 48), S(23, 48), S(23, 48), S(23, 48), S(20, 48), S(13, 43) }, { S(13, 43), S(20, 48), S(23, 48), S(23, 48), S(23, 48), S(23, 48), S(20, 48), S(13, 43) }}; // Isolated pawn penalty by opposed flag and file - const Score IsolatedPawnPenalty[2][8] = { + const Score IsolatedPawnPenalty[2][FILE_NB] = { { S(37, 45), S(54, 52), S(60, 52), S(60, 52), S(60, 52), S(60, 52), S(54, 52), S(37, 45) }, { S(25, 30), S(36, 35), S(40, 35), S(40, 35), S(40, 35), S(40, 35), S(36, 35), S(25, 30) }}; // Backward pawn penalty by opposed flag and file - const Score BackwardPawnPenalty[2][8] = { + const Score BackwardPawnPenalty[2][FILE_NB] = { { S(30, 42), S(43, 46), S(49, 46), S(49, 46), S(49, 46), S(49, 46), S(43, 46), S(30, 42) }, { S(20, 28), S(29, 31), S(33, 31), S(33, 31), S(33, 31), S(33, 31), S(29, 31), S(20, 28) }}; // Pawn chain membership bonus by file - const Score ChainBonus[8] = { + const Score ChainBonus[FILE_NB] = { S(11,-1), S(13,-1), S(13,-1), S(14,-1), S(14,-1), S(13,-1), S(13,-1), S(11,-1) }; // Candidate passed pawn bonus by rank - const Score CandidateBonus[8] = { + const Score CandidateBonus[RANK_NB] = { S( 0, 0), S( 6, 13), S(6,13), S(14,29), S(34,68), S(83,166), S(0, 0), S( 0, 0) }; @@ -65,12 +65,12 @@ namespace { const Score PawnStructureWeight = S(233, 201); // Weakness of our pawn shelter in front of the king indexed by [king pawn][rank] - const Value ShelterWeakness[2][8] = + const Value ShelterWeakness[2][RANK_NB] = { { V(141), V(0), V(38), V(102), V(128), V(141), V(141) }, { V( 61), V(0), V(16), V( 44), V( 56), V( 61), V( 61) } }; // Danger of enemy pawns moving toward our king indexed by [pawn blocked][rank] - const Value StormDanger[2][8] = + const Value StormDanger[2][RANK_NB] = { { V(26), V(0), V(128), V(51), V(26) }, { V(13), V(0), V( 64), V(25), V(13) } }; @@ -80,18 +80,122 @@ namespace { #undef S #undef V + + template + Score evaluate_pawns(const Position& pos, Bitboard ourPawns, + Bitboard theirPawns, Pawns::Entry* e) { + + const Color Them = (Us == WHITE ? BLACK : WHITE); + + Bitboard b; + Square s; + File f; + Rank r; + bool passed, isolated, doubled, opposed, chain, backward, candidate; + Score value = SCORE_ZERO; + const Square* pl = pos.piece_list(Us, PAWN); + + // Loop through all pawns of the current color and score each pawn + while ((s = *pl++) != SQ_NONE) + { + assert(pos.piece_on(s) == make_piece(Us, PAWN)); + + f = file_of(s); + r = rank_of(s); + + // This file cannot be half open + e->halfOpenFiles[Us] &= ~(1 << f); + + // Our rank plus previous one. Used for chain detection + b = rank_bb(r) | rank_bb(Us == WHITE ? r - Rank(1) : r + Rank(1)); + + // Flag the pawn as passed, isolated, doubled or member of a pawn + // chain (but not the backward one). + chain = ourPawns & adjacent_files_bb(f) & b; + isolated = !(ourPawns & adjacent_files_bb(f)); + doubled = ourPawns & forward_bb(Us, s); + opposed = theirPawns & forward_bb(Us, s); + passed = !(theirPawns & passed_pawn_mask(Us, s)); + + // Test for backward pawn + backward = false; + + // If the pawn is passed, isolated, or member of a pawn chain it cannot + // be backward. If there are friendly pawns behind on adjacent files + // or if can capture an enemy pawn it cannot be backward either. + if ( !(passed | isolated | chain) + && !(ourPawns & attack_span_mask(Them, s)) + && !(pos.attacks_from(s, Us) & theirPawns)) + { + // We now know that there are no friendly pawns beside or behind this + // pawn on adjacent files. We now check whether the pawn is + // backward by looking in the forward direction on the adjacent + // files, and seeing whether we meet a friendly or an enemy pawn first. + b = pos.attacks_from(s, Us); + + // Note that we are sure to find something because pawn is not passed + // nor isolated, so loop is potentially infinite, but it isn't. + while (!(b & (ourPawns | theirPawns))) + Us == WHITE ? b <<= 8 : b >>= 8; + + // The friendly pawn needs to be at least two ranks closer than the + // enemy pawn in order to help the potentially backward pawn advance. + backward = (b | (Us == WHITE ? b << 8 : b >> 8)) & theirPawns; + } + + assert(opposed | passed | (attack_span_mask(Us, s) & theirPawns)); + + // A not passed pawn is a candidate to become passed if it is free to + // advance and if the number of friendly pawns beside or behind this + // pawn on adjacent files is higher or equal than the number of + // enemy pawns in the forward direction on the adjacent files. + candidate = !(opposed | passed | backward | isolated) + && (b = attack_span_mask(Them, s + pawn_push(Us)) & ourPawns) != 0 + && popcount(b) >= popcount(attack_span_mask(Us, s) & theirPawns); + + // Passed pawns will be properly scored in evaluation because we need + // full attack info to evaluate passed pawns. Only the frontmost passed + // pawn on each file is considered a true passed pawn. + if (passed && !doubled) + e->passedPawns[Us] |= s; + + // Score this pawn + if (isolated) + value -= IsolatedPawnPenalty[opposed][f]; + + if (doubled) + value -= DoubledPawnPenalty[opposed][f]; + + if (backward) + value -= BackwardPawnPenalty[opposed][f]; + + if (chain) + value += ChainBonus[f]; + + if (candidate) + value += CandidateBonus[relative_rank(Us, s)]; + } + + e->pawnsOnSquares[Us][BLACK] = popcount(ourPawns & BlackSquares); + e->pawnsOnSquares[Us][WHITE] = pos.piece_count(Us, PAWN) - e->pawnsOnSquares[Us][BLACK]; + + e->pawnsOnSquares[Them][BLACK] = popcount(theirPawns & BlackSquares); + e->pawnsOnSquares[Them][WHITE] = pos.piece_count(Them, PAWN) - e->pawnsOnSquares[Them][BLACK]; + + return value; + } } +namespace Pawns { -/// PawnTable::probe() takes a position object as input, computes a PawnEntry -/// 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. +/// probe() takes a position object as input, computes a Entry object, and returns +/// a pointer to it. The result is also stored in a hash table, so we don't have +/// to recompute everything when the same pawn structure occurs again. -PawnEntry* PawnTable::probe(const Position& pos) { +Entry* probe(const Position& pos, Table& entries) { Key key = pos.pawn_key(); - PawnEntry* e = entries[key]; + Entry* e = entries[key]; // If e->key matches the position's pawn hash key, it means that we // have analysed this pawn structure before, and we can simply return @@ -118,112 +222,11 @@ PawnEntry* PawnTable::probe(const Position& pos) { } -/// PawnTable::evaluate_pawns() evaluates each pawn of the given color - -template -Score PawnTable::evaluate_pawns(const Position& pos, Bitboard ourPawns, - Bitboard theirPawns, PawnEntry* e) { - - const Color Them = (Us == WHITE ? BLACK : WHITE); - - Bitboard b; - Square s; - File f; - Rank r; - bool passed, isolated, doubled, opposed, chain, backward, candidate; - Score value = SCORE_ZERO; - const Square* pl = pos.piece_list(Us, PAWN); - - // Loop through all pawns of the current color and score each pawn - while ((s = *pl++) != SQ_NONE) - { - assert(pos.piece_on(s) == make_piece(Us, PAWN)); - - f = file_of(s); - r = rank_of(s); - - // This file cannot be half open - e->halfOpenFiles[Us] &= ~(1 << f); - - // Our rank plus previous one. Used for chain detection - b = rank_bb(r) | rank_bb(Us == WHITE ? r - Rank(1) : r + Rank(1)); - - // Flag the pawn as passed, isolated, doubled or member of a pawn - // chain (but not the backward one). - chain = ourPawns & adjacent_files_bb(f) & b; - isolated = !(ourPawns & adjacent_files_bb(f)); - doubled = ourPawns & forward_bb(Us, s); - opposed = theirPawns & forward_bb(Us, s); - passed = !(theirPawns & passed_pawn_mask(Us, s)); - - // Test for backward pawn - backward = false; - - // If the pawn is passed, isolated, or member of a pawn chain it cannot - // be backward. If there are friendly pawns behind on adjacent files - // or if can capture an enemy pawn it cannot be backward either. - if ( !(passed | isolated | chain) - && !(ourPawns & attack_span_mask(Them, s)) - && !(pos.attacks_from(s, Us) & theirPawns)) - { - // We now know that there are no friendly pawns beside or behind this - // pawn on adjacent files. We now check whether the pawn is - // backward by looking in the forward direction on the adjacent - // files, and seeing whether we meet a friendly or an enemy pawn first. - b = pos.attacks_from(s, Us); - - // Note that we are sure to find something because pawn is not passed - // nor isolated, so loop is potentially infinite, but it isn't. - while (!(b & (ourPawns | theirPawns))) - Us == WHITE ? b <<= 8 : b >>= 8; - - // The friendly pawn needs to be at least two ranks closer than the - // enemy pawn in order to help the potentially backward pawn advance. - backward = (b | (Us == WHITE ? b << 8 : b >> 8)) & theirPawns; - } - - assert(opposed | passed | (attack_span_mask(Us, s) & theirPawns)); - - // A not passed pawn is a candidate to become passed if it is free to - // advance and if the number of friendly pawns beside or behind this - // pawn on adjacent files is higher or equal than the number of - // enemy pawns in the forward direction on the adjacent files. - candidate = !(opposed | passed | backward | isolated) - && (b = attack_span_mask(Them, s + pawn_push(Us)) & ourPawns) != 0 - && popcount(b) >= popcount(attack_span_mask(Us, s) & theirPawns); - - // Passed pawns will be properly scored in evaluation because we need - // full attack info to evaluate passed pawns. Only the frontmost passed - // pawn on each file is considered a true passed pawn. - if (passed && !doubled) - e->passedPawns[Us] |= s; - - // Score this pawn - if (isolated) - value -= IsolatedPawnPenalty[opposed][f]; - - if (doubled) - value -= DoubledPawnPenalty[opposed][f]; - - if (backward) - value -= BackwardPawnPenalty[opposed][f]; - - if (chain) - value += ChainBonus[f]; - - if (candidate) - value += CandidateBonus[relative_rank(Us, s)]; - } - - return value; -} - - -/// PawnEntry::shelter_storm() calculates shelter and storm penalties for the file +/// Entry::shelter_storm() calculates shelter and storm penalties for the file /// the king is on, as well as the two adjacent files. template -Value PawnEntry::shelter_storm(const Position& pos, Square ksq) { +Value Entry::shelter_storm(const Position& pos, Square ksq) { const Color Them = (Us == WHITE ? BLACK : WHITE); @@ -234,7 +237,7 @@ Value PawnEntry::shelter_storm(const Position& pos, Square ksq) { Rank rkUs, rkThem; File kf = file_of(ksq); - kf = (kf == FILE_A) ? kf++ : (kf == FILE_H) ? kf-- : kf; + kf = (kf == FILE_A) ? FILE_B : (kf == FILE_H) ? FILE_G : kf; for (int f = kf - 1; f <= kf + 1; f++) { @@ -253,11 +256,11 @@ Value PawnEntry::shelter_storm(const Position& pos, Square ksq) { } -/// PawnEntry::update_safety() calculates and caches a bonus for king safety. It is +/// Entry::update_safety() calculates and caches a bonus for king safety. It is /// called only when king square changes, about 20% of total king_safety() calls. template -Score PawnEntry::update_safety(const Position& pos, Square ksq) { +Score Entry::update_safety(const Position& pos, Square ksq) { kingSquares[Us] = ksq; castleRights[Us] = pos.can_castle(Us); @@ -283,5 +286,7 @@ Score PawnEntry::update_safety(const Position& pos, Square ksq) { } // Explicit template instantiation -template Score PawnEntry::update_safety(const Position& pos, Square ksq); -template Score PawnEntry::update_safety(const Position& pos, Square ksq); +template Score Entry::update_safety(const Position& pos, Square ksq); +template Score Entry::update_safety(const Position& pos, Square ksq); + +} // namespace Pawns diff --git a/DroidFish/jni/stockfish/pawns.h b/DroidFish/jni/stockfish/pawns.h index e6bc2c3..293869b 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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,31 +24,31 @@ #include "position.h" #include "types.h" -const int PawnTableSize = 16384; +namespace Pawns { -/// PawnEntry is a class which contains various information about a pawn -/// structure. Currently, it only includes a middle game and an end game -/// pawn structure evaluation, and a bitboard of passed pawns. We may want -/// to add further information in the future. A lookup to the pawn hash -/// table (performed by calling the probe method in a PawnTable object) -/// returns a pointer to a PawnEntry object. +/// Pawns::Entry contains various information about a pawn structure. Currently, +/// it only includes a middle game and end game pawn structure evaluation, and a +/// bitboard of passed pawns. We may want to add further information in the future. +/// A lookup to the pawn hash table (performed by calling the probe function) +/// returns a pointer to an Entry object. -class PawnEntry { +struct Entry { - friend struct PawnTable; - -public: - Score pawns_value() const; - Bitboard pawn_attacks(Color c) const; - Bitboard passed_pawns(Color c) const; - int file_is_half_open(Color c, File f) const; - int has_open_file_to_left(Color c, File f) const; - int has_open_file_to_right(Color c, File f) const; + Score pawns_value() const { return value; } + Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; } + Bitboard passed_pawns(Color c) const { return passedPawns[c]; } + int file_is_half_open(Color c, File f) const { return halfOpenFiles[c] & (1 << int(f)); } + int has_open_file_to_left(Color c, File f) const { return halfOpenFiles[c] & ((1 << int(f)) - 1); } + int has_open_file_to_right(Color c, File f) const { return halfOpenFiles[c] & ~((1 << int(f+1)) - 1); } + int pawns_on_same_color_squares(Color c, Square s) const { return pawnsOnSquares[c][!!(BlackSquares & s)]; } template - Score king_safety(const Position& pos, Square ksq); + Score king_safety(const Position& pos, Square ksq) { + + return kingSquares[Us] == ksq && castleRights[Us] == pos.can_castle(Us) + ? kingSafety[Us] : update_safety(pos, ksq); + } -private: template Score update_safety(const Position& pos, Square ksq); @@ -56,60 +56,21 @@ private: Value shelter_storm(const Position& pos, Square ksq); Key key; - Bitboard passedPawns[2]; - Bitboard pawnAttacks[2]; - Square kingSquares[2]; - int minKPdistance[2]; - int castleRights[2]; + Bitboard passedPawns[COLOR_NB]; + Bitboard pawnAttacks[COLOR_NB]; + Square kingSquares[COLOR_NB]; + int minKPdistance[COLOR_NB]; + int castleRights[COLOR_NB]; Score value; - int halfOpenFiles[2]; - Score kingSafety[2]; + int halfOpenFiles[COLOR_NB]; + Score kingSafety[COLOR_NB]; + int pawnsOnSquares[COLOR_NB][COLOR_NB]; }; +typedef HashTable Table; -/// The PawnTable class represents a pawn hash table. The most important -/// method is probe, which returns a pointer to a PawnEntry object. +Entry* probe(const Position& pos, Table& entries); -struct PawnTable { - - PawnEntry* probe(const Position& pos); - - template - static Score evaluate_pawns(const Position& pos, Bitboard ourPawns, - Bitboard theirPawns, PawnEntry* e); - - HashTable entries; -}; - - -inline Score PawnEntry::pawns_value() const { - return value; -} - -inline Bitboard PawnEntry::pawn_attacks(Color c) const { - return pawnAttacks[c]; -} - -inline Bitboard PawnEntry::passed_pawns(Color c) const { - return passedPawns[c]; -} - -inline int PawnEntry::file_is_half_open(Color c, File f) const { - return halfOpenFiles[c] & (1 << int(f)); -} - -inline int PawnEntry::has_open_file_to_left(Color c, File f) const { - return halfOpenFiles[c] & ((1 << int(f)) - 1); -} - -inline int PawnEntry::has_open_file_to_right(Color c, File f) const { - return halfOpenFiles[c] & ~((1 << int(f+1)) - 1); -} - -template -inline Score PawnEntry::king_safety(const Position& pos, Square ksq) { - return kingSquares[Us] == ksq && castleRights[Us] == pos.can_castle(Us) - ? kingSafety[Us] : update_safety(pos, ksq); } #endif // !defined(PAWNS_H_INCLUDED) diff --git a/DroidFish/jni/stockfish/platform.h b/DroidFish/jni/stockfish/platform.h index ffa00a5..ad02575 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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,6 +40,7 @@ typedef unsigned __int64 uint64_t; #else # include +# include // Used by sysconf(_SC_NPROCESSORS_ONLN) #endif #if !defined(_WIN32) && !defined(_WIN64) // Linux - Unix @@ -92,6 +93,9 @@ typedef CRITICAL_SECTION Lock; typedef HANDLE WaitCondition; typedef HANDLE NativeHandle; +// On Windows 95 and 98 parameter lpThreadId my not be null +inline DWORD* dwWin9xKludge() { static DWORD dw; return &dw; } + # define lock_init(x) InitializeCriticalSection(&(x)) # define lock_grab(x) EnterCriticalSection(&(x)) # define lock_release(x) LeaveCriticalSection(&(x)) @@ -101,7 +105,7 @@ typedef HANDLE NativeHandle; # define cond_signal(x) SetEvent(x) # define cond_wait(x,y) { lock_release(y); WaitForSingleObject(x, INFINITE); lock_grab(y); } # define cond_timedwait(x,y,z) { lock_release(y); WaitForSingleObject(x,z); lock_grab(y); } -# define thread_create(x,f,t) (x = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)f,t,0,NULL), x != NULL) +# define thread_create(x,f,t) (x = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)f,t,0,dwWin9xKludge()), x != NULL) # define thread_join(x) { WaitForSingleObject(x, INFINITE); CloseHandle(x); } #endif diff --git a/DroidFish/jni/stockfish/position.cpp b/DroidFish/jni/stockfish/position.cpp index 4bc8df5..210a1ba 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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 #include #include #include @@ -40,16 +41,16 @@ static const string PieceToChar(" PNBRQK pnbrqk"); CACHE_LINE_ALIGNMENT -Score pieceSquareTable[16][64]; // [piece][square] -Value PieceValue[2][18] = { // [Mg / Eg][piece / pieceType] +Score pieceSquareTable[PIECE_NB][SQUARE_NB]; +Value PieceValue[PHASE_NB][PIECE_NB] = { { VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg }, { VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } }; namespace Zobrist { -Key psq[2][8][64]; // [color][pieceType][square / piece count] -Key enpassant[8]; // [file] -Key castle[16]; // [castleRight] +Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; +Key enpassant[FILE_NB]; +Key castle[CASTLE_RIGHT_NB]; Key side; Key exclusion; @@ -86,10 +87,10 @@ void init() { for (PieceType pt = PAWN; pt <= KING; pt++) { - PieceValue[Mg][make_piece(BLACK, pt)] = PieceValue[Mg][pt]; - PieceValue[Eg][make_piece(BLACK, pt)] = PieceValue[Eg][pt]; + PieceValue[MG][make_piece(BLACK, pt)] = PieceValue[MG][pt]; + PieceValue[EG][make_piece(BLACK, pt)] = PieceValue[EG][pt]; - Score v = make_score(PieceValue[Mg][pt], PieceValue[Eg][pt]); + Score v = make_score(PieceValue[MG][pt], PieceValue[EG][pt]); for (Square s = SQ_A1; s <= SQ_H8; s++) { @@ -172,11 +173,11 @@ Position& Position::operator=(const Position& pos) { } -/// Position::from_fen() initializes the position object with the given FEN -/// string. This function is not very robust - make sure that input FENs are -/// correct (this is assumed to be the responsibility of the GUI). +/// Position::set() initializes the position object with the given FEN string. +/// This function is not very robust - make sure that input FENs are correct, +/// this is assumed to be the responsibility of the GUI. -void Position::from_fen(const string& fenStr, bool isChess960, Thread* th) { +void Position::set(const string& fenStr, bool isChess960, Thread* th) { /* A FEN string defines a particular position using only the ASCII character set. @@ -214,13 +215,13 @@ void Position::from_fen(const string& fenStr, bool isChess960, Thread* th) { char col, row, token; size_t p; Square sq = SQ_A8; - std::istringstream fen(fenStr); + std::istringstream ss(fenStr); clear(); - fen >> std::noskipws; + ss >> std::noskipws; // 1. Piece placement - while ((fen >> token) && !isspace(token)) + while ((ss >> token) && !isspace(token)) { if (isdigit(token)) sq += Square(token - '0'); // Advance the given number of files @@ -236,16 +237,16 @@ void Position::from_fen(const string& fenStr, bool isChess960, Thread* th) { } // 2. Active color - fen >> token; + ss >> token; sideToMove = (token == 'w' ? WHITE : BLACK); - fen >> token; + ss >> token; // 3. Castling availability. Compatible with 3 standards: Normal FEN standard, // Shredder-FEN that uses the letters of the columns on which the rooks began // the game instead of KQkq and also X-FEN standard that, in case of Chess960, // if an inner rook is associated with the castling right, the castling tag is // replaced by the file letter of the involved rook, as for the Shredder-FEN. - while ((fen >> token) && !isspace(token)) + while ((ss >> token) && !isspace(token)) { Square rsq; Color c = islower(token) ? BLACK : WHITE; @@ -268,8 +269,8 @@ void Position::from_fen(const string& fenStr, bool isChess960, Thread* th) { } // 4. En passant square. Ignore if no pawn capture is possible - if ( ((fen >> col) && (col >= 'a' && col <= 'h')) - && ((fen >> row) && (row == '3' || row == '6'))) + if ( ((ss >> col) && (col >= 'a' && col <= 'h')) + && ((ss >> row) && (row == '3' || row == '6'))) { st->epSquare = File(col - 'a') | Rank(row - '1'); @@ -278,11 +279,11 @@ void Position::from_fen(const string& fenStr, bool isChess960, Thread* th) { } // 5-6. Halfmove clock and fullmove number - fen >> std::skipws >> st->rule50 >> startPosPly; + ss >> std::skipws >> st->rule50 >> gamePly; // Convert from fullmove starting from 1 to ply starting from 0, // handle also common incorrect FEN with fullmove = 0. - startPosPly = std::max(2 * (startPosPly - 1), 0) + int(sideToMove == BLACK); + gamePly = std::max(2 * (gamePly - 1), 0) + int(sideToMove == BLACK); st->key = compute_key(); st->pawnKey = compute_pawn_key(); @@ -325,71 +326,64 @@ void Position::set_castle_right(Color c, Square rfrom) { } -/// Position::to_fen() returns a FEN representation of the position. In case +/// Position::fen() returns a FEN representation of the position. In case /// of Chess960 the Shredder-FEN notation is used. Mainly a debugging function. -const string Position::to_fen() const { +const string Position::fen() const { - std::ostringstream fen; - Square sq; - int emptyCnt; + std::ostringstream ss; for (Rank rank = RANK_8; rank >= RANK_1; rank--) { - emptyCnt = 0; - for (File file = FILE_A; file <= FILE_H; file++) { - sq = file | rank; + Square sq = file | rank; if (is_empty(sq)) - emptyCnt++; - else { - if (emptyCnt > 0) - { - fen << emptyCnt; - emptyCnt = 0; - } - fen << PieceToChar[piece_on(sq)]; + int emptyCnt = 1; + + for ( ; file < FILE_H && is_empty(sq++); file++) + emptyCnt++; + + ss << emptyCnt; } + else + ss << PieceToChar[piece_on(sq)]; } - if (emptyCnt > 0) - fen << emptyCnt; - if (rank > RANK_1) - fen << '/'; + ss << '/'; } - fen << (sideToMove == WHITE ? " w " : " b "); + ss << (sideToMove == WHITE ? " w " : " b "); if (can_castle(WHITE_OO)) - fen << (chess960 ? char(toupper(file_to_char(file_of(castle_rook_square(WHITE, KING_SIDE))))) : 'K'); + ss << (chess960 ? file_to_char(file_of(castle_rook_square(WHITE, KING_SIDE)), false) : 'K'); if (can_castle(WHITE_OOO)) - fen << (chess960 ? char(toupper(file_to_char(file_of(castle_rook_square(WHITE, QUEEN_SIDE))))) : 'Q'); + ss << (chess960 ? file_to_char(file_of(castle_rook_square(WHITE, QUEEN_SIDE)), false) : 'Q'); if (can_castle(BLACK_OO)) - fen << (chess960 ? file_to_char(file_of(castle_rook_square(BLACK, KING_SIDE))) : 'k'); + ss << (chess960 ? file_to_char(file_of(castle_rook_square(BLACK, KING_SIDE)), true) : 'k'); if (can_castle(BLACK_OOO)) - fen << (chess960 ? file_to_char(file_of(castle_rook_square(BLACK, QUEEN_SIDE))) : 'q'); + ss << (chess960 ? file_to_char(file_of(castle_rook_square(BLACK, QUEEN_SIDE)), true) : 'q'); if (st->castleRights == CASTLES_NONE) - fen << '-'; + ss << '-'; - fen << (ep_square() == SQ_NONE ? " - " : " " + square_to_string(ep_square()) + " ") - << st->rule50 << " " << 1 + (startPosPly - int(sideToMove == BLACK)) / 2; + ss << (ep_square() == SQ_NONE ? " - " : " " + square_to_string(ep_square()) + " ") + << st->rule50 << " " << 1 + (gamePly - int(sideToMove == BLACK)) / 2; - return fen.str(); + return ss.str(); } -/// Position::print() prints an ASCII representation of the position to -/// the standard output. If a move is given then also the san is printed. +/// Position::pretty() returns an ASCII representation of the position to be +/// printed to the standard output together with the move's san notation. -void Position::print(Move move) const { +const string Position::pretty(Move move) const { const string dottedLine = "\n+---+---+---+---+---+---+---+---+"; const string twoRows = dottedLine + "\n| | . | | . | | . | | . |" @@ -397,19 +391,27 @@ void Position::print(Move move) const { string brd = twoRows + twoRows + twoRows + twoRows + dottedLine; - sync_cout; + std::ostringstream ss; if (move) - { - Position p(*this); - cout << "\nMove is: " << (sideToMove == BLACK ? ".." : "") << move_to_san(p, move); - } + ss << "\nMove: " << (sideToMove == BLACK ? ".." : "") + << move_to_san(*const_cast(this), move); for (Square sq = SQ_A1; sq <= SQ_H8; sq++) if (piece_on(sq) != NO_PIECE) brd[513 - 68*rank_of(sq) + 4*file_of(sq)] = PieceToChar[piece_on(sq)]; - cout << brd << "\nFen is: " << to_fen() << "\nKey is: " << st->key << sync_endl; + ss << brd << "\nFen: " << fen() << "\nKey: " << std::hex << std::uppercase + << std::setfill('0') << std::setw(16) << st->key << "\nCheckers: "; + + for (Bitboard b = checkers(); b; ) + ss << square_to_string(pop_lsb(&b)) << " "; + + ss << "\nLegal moves: "; + for (MoveList ml(*this); !ml.end(); ++ml) + ss << move_to_san(*const_cast(this), ml.move()) << " "; + + return ss.str(); } @@ -475,37 +477,6 @@ Bitboard Position::attacks_from(Piece p, Square s, Bitboard occ) { } -/// Position::move_attacks_square() tests whether a move from the current -/// position attacks a given square. - -bool Position::move_attacks_square(Move m, Square s) const { - - assert(is_ok(m)); - assert(is_ok(s)); - - Bitboard occ, xray; - Square from = from_sq(m); - Square to = to_sq(m); - Piece piece = piece_moved(m); - - assert(!is_empty(from)); - - // Update occupancy as if the piece is moving - occ = pieces() ^ from ^ to; - - // The piece moved in 'to' attacks the square 's' ? - if (attacks_from(piece, to, occ) & s) - return true; - - // Scan for possible X-ray attackers behind the moved piece - xray = (attacks_bb< ROOK>(s, occ) & pieces(color_of(piece), QUEEN, ROOK)) - | (attacks_bb(s, occ) & pieces(color_of(piece), QUEEN, BISHOP)); - - // Verify attackers are triggered by our move and not already existing - return xray && (xray ^ (xray & attacks_from(s))); -} - - /// Position::pl_move_is_legal() tests whether a pseudo-legal move is legal bool Position::pl_move_is_legal(Move m, Bitboard pinned) const { @@ -553,20 +524,6 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const { } -/// Position::move_is_legal() takes a random move and tests whether the move -/// is legal. This version is not very fast and should be used only in non -/// time-critical paths. - -bool Position::move_is_legal(const Move m) const { - - for (MoveList ml(*this); !ml.end(); ++ml) - if (ml.move() == m) - return true; - - return false; -} - - /// Position::is_pseudo_legal() takes a random move and tests whether the move /// is pseudo legal. It is used to validate moves from TT that can be corrupted /// due to SMP concurrent access or hash position key aliasing. @@ -574,14 +531,13 @@ bool Position::move_is_legal(const Move m) const { bool Position::is_pseudo_legal(const Move m) const { Color us = sideToMove; - Color them = ~sideToMove; Square from = from_sq(m); Square to = to_sq(m); Piece pc = piece_moved(m); // Use a slower but simpler function for uncommon cases if (type_of(m) != NORMAL) - return move_is_legal(m); + return MoveList(*this).contains(m); // Is not a promotion, so promotion piece must be empty if (promotion_type(m) - 2 != NO_PIECE_TYPE) @@ -593,7 +549,7 @@ bool Position::is_pseudo_legal(const Move m) const { return false; // The destination square cannot be occupied by a friendly piece - if (color_of(piece_on(to)) == us) + if (piece_on(to) != NO_PIECE && color_of(piece_on(to)) == us) return false; // Handle the special case of a pawn move @@ -619,7 +575,7 @@ bool Position::is_pseudo_legal(const Move m) const { case DELTA_SE: // Capture. The destination square must be occupied by an enemy // piece (en passant captures was handled earlier). - if (color_of(piece_on(to)) != them) + if (piece_on(to) == NO_PIECE || color_of(piece_on(to)) != ~us) return false; // From and to files must be one file apart, avoids a7h5 @@ -664,18 +620,16 @@ bool Position::is_pseudo_legal(const Move m) const { // Evasions generator already takes care to avoid some kind of illegal moves // and pl_move_is_legal() relies on this. So we have to take care that the // same kind of moves are filtered out here. - if (in_check()) + if (checkers()) { if (type_of(pc) != KING) { - Bitboard b = checkers(); - Square checksq = pop_lsb(&b); - - if (b) // double check ? In this case a king move is required + // Double check? In this case a king move is required + if (more_than_one(checkers())) return false; // Our move must be a blocking evasion or a capture of the checking piece - if (!((between_bb(checksq, king_square(us)) | checkers()) & to)) + if (!((between_bb(lsb(checkers()), king_square(us)) | checkers()) & to)) return false; } // In case of king moves under check we have to remove king so to catch @@ -720,15 +674,16 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const { Color us = sideToMove; Square ksq = king_square(~us); - // Promotion with check ? - if (type_of(m) == PROMOTION) + switch (type_of(m)) + { + case PROMOTION: return attacks_from(Piece(promotion_type(m)), to, pieces() ^ from) & ksq; // En passant capture with check ? We have already handled the case // of direct checks and ordinary discovered check, the only case we // need to handle is the unusual case of a discovered check through // the captured pawn. - if (type_of(m) == ENPASSANT) + case ENPASSANT: { Square capsq = file_of(to) | rank_of(from); Bitboard b = (pieces() ^ from ^ capsq) | to; @@ -736,9 +691,7 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const { return (attacks_bb< ROOK>(ksq, b) & pieces(us, QUEEN, ROOK)) | (attacks_bb(ksq, b) & pieces(us, QUEEN, BISHOP)); } - - // Castling with check ? - if (type_of(m) == CASTLE) + case CASTLE: { Square kfrom = from; Square rfrom = to; // 'King captures the rook' notation @@ -748,8 +701,10 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const { return attacks_bb(rto, b) & ksq; } - - return false; + default: + assert(false); + return false; + } } @@ -772,9 +727,9 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI Key k = st->key; // Copy some fields of old state to our new StateInfo object except the ones - // which are recalculated from scratch anyway, then switch our state pointer - // to point to the new, ready to be updated, state. - memcpy(&newSt, st, sizeof(ReducedStateInfo)); + // which are going to be recalculated from scratch anyway, then switch our state + // pointer to point to the new, ready to be updated, state. + memcpy(&newSt, st, StateCopySize64 * sizeof(uint64_t)); newSt.previous = st; st = &newSt; @@ -782,18 +737,12 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI // Update side to move k ^= Zobrist::side; - // Increment the 50 moves rule draw counter. Resetting it to zero in the - // case of a capture or a pawn move is taken care of later. + // Increment ply counters.In particular rule50 will be later reset it to zero + // in case of a capture or a pawn move. + gamePly++; st->rule50++; st->pliesFromNull++; - if (type_of(m) == CASTLE) - { - st->key = k; - do_castle_move(m); - return; - } - Color us = sideToMove; Color them = ~us; Square from = from_sq(m); @@ -803,9 +752,25 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI PieceType capture = type_of(m) == ENPASSANT ? PAWN : type_of(piece_on(to)); assert(color_of(piece) == us); - assert(color_of(piece_on(to)) != us); + assert(piece_on(to) == NO_PIECE || color_of(piece_on(to)) == them || type_of(m) == CASTLE); assert(capture != KING); + if (type_of(m) == CASTLE) + { + assert(piece == make_piece(us, KING)); + + bool kingSide = to > from; + Square rfrom = to; // Castle is encoded as "king captures friendly rook" + Square rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1); + to = relative_square(us, kingSide ? SQ_G1 : SQ_C1); + capture = NO_PIECE_TYPE; + + do_castle(from, to, rfrom, rto); + + st->psqScore += psq_delta(make_piece(us, ROOK), rfrom, rto); + k ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto]; + } + if (capture) { Square capsq = to; @@ -830,7 +795,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][capture]; + st->npMaterial[them] -= PieceValue[MG][capture]; // Remove the captured piece byTypeBB[ALL_PIECES] ^= capsq; @@ -840,7 +805,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI // Update piece list, move the last piece at index[capsq] position and // shrink the list. // - // WARNING: This is a not revresible operation. When we will reinsert the + // WARNING: This is a not reversible operation. When we will reinsert the // captured piece in undo_move() we will put it at the end of the list and // not in its original place, it means index[] and pieceList[] are not // guaranteed to be invariant to a do_move() + undo_move() sequence. @@ -849,9 +814,10 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI pieceList[them][capture][index[lastSquare]] = lastSquare; pieceList[them][capture][pieceCount[them][capture]] = SQ_NONE; - // Update hash keys + // Update material hash key and prefetch access to materialTable k ^= Zobrist::psq[them][capture][capsq]; st->materialKey ^= Zobrist::psq[them][capture][pieceCount[them][capture]]; + prefetch((char*)thisThread->materialTable[st->materialKey]); // Update incremental scores st->psqScore -= pieceSquareTable[make_piece(them, capture)][capsq]; @@ -878,22 +844,25 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI st->castleRights &= ~cr; } - // Prefetch TT access as soon as we know key is updated + // Prefetch TT access as soon as we know the new hash key prefetch((char*)TT.first_entry(k)); - // Move the piece - Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to]; - byTypeBB[ALL_PIECES] ^= from_to_bb; - byTypeBB[pt] ^= from_to_bb; - byColorBB[us] ^= from_to_bb; + // Move the piece. The tricky Chess960 castle is handled earlier + if (type_of(m) != CASTLE) + { + Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to]; + byTypeBB[ALL_PIECES] ^= from_to_bb; + byTypeBB[pt] ^= from_to_bb; + byColorBB[us] ^= from_to_bb; - board[to] = board[from]; - board[from] = NO_PIECE; + board[from] = NO_PIECE; + board[to] = piece; - // Update piece lists, index[from] is not updated and becomes stale. This - // works as long as index[] is accessed just by known occupied squares. - index[to] = index[from]; - pieceList[us][pt][index[to]] = to; + // Update piece lists, index[from] is not updated and becomes stale. This + // works as long as index[] is accessed just by known occupied squares. + index[to] = index[from]; + pieceList[us][pt][index[to]] = to; + } // If the moving piece is a pawn do some special extra work if (pt == PAWN) @@ -938,20 +907,17 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI - pieceSquareTable[make_piece(us, PAWN)][to]; // Update material - st->npMaterial[us] += PieceValue[Mg][promotion]; + st->npMaterial[us] += PieceValue[MG][promotion]; } - // Update pawn hash key + // Update pawn hash key and prefetch access to pawnsTable st->pawnKey ^= Zobrist::psq[us][PAWN][from] ^ Zobrist::psq[us][PAWN][to]; + prefetch((char*)thisThread->pawnsTable[st->pawnKey]); // Reset rule 50 draw counter st->rule50 = 0; } - // Prefetch pawn and material hash tables - prefetch((char*)thisThread->pawnTable.entries[st->pawnKey]); - prefetch((char*)thisThread->materialTable.entries[st->materialKey]); - // Update incremental scores st->psqScore += psq_delta(piece, from, to); @@ -1001,22 +967,14 @@ void Position::undo_move(Move m) { sideToMove = ~sideToMove; - if (type_of(m) == CASTLE) - { - do_castle_move(m); - return; - } - Color us = sideToMove; Color them = ~us; Square from = from_sq(m); Square to = to_sq(m); - Piece piece = piece_on(to); - PieceType pt = type_of(piece); + PieceType pt = type_of(piece_on(to)); PieceType capture = st->capturedType; - assert(is_empty(from)); - assert(color_of(piece) == us); + assert(is_empty(from) || type_of(m) == CASTLE); assert(capture != KING); if (type_of(m) == PROMOTION) @@ -1044,19 +1002,32 @@ void Position::undo_move(Move m) { pt = PAWN; } - // Put the piece back at the source square - Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to]; - byTypeBB[ALL_PIECES] ^= from_to_bb; - byTypeBB[pt] ^= from_to_bb; - byColorBB[us] ^= from_to_bb; + if (type_of(m) == CASTLE) + { + bool kingSide = to > from; + Square rfrom = to; // Castle is encoded as "king captures friendly rook" + Square rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1); + to = relative_square(us, kingSide ? SQ_G1 : SQ_C1); + capture = NO_PIECE_TYPE; + pt = KING; + do_castle(to, from, rto, rfrom); + } + else + { + // Put the piece back at the source square + Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to]; + byTypeBB[ALL_PIECES] ^= from_to_bb; + byTypeBB[pt] ^= from_to_bb; + byColorBB[us] ^= from_to_bb; - board[from] = board[to]; - board[to] = NO_PIECE; + board[to] = NO_PIECE; + board[from] = make_piece(us, pt); - // Update piece lists, index[to] is not updated and becomes stale. This - // works as long as index[] is accessed just by known occupied squares. - index[from] = index[to]; - pieceList[us][pt][index[from]] = from; + // Update piece lists, index[to] is not updated and becomes stale. This + // works as long as index[] is accessed just by known occupied squares. + index[from] = index[to]; + pieceList[us][pt][index[from]] = from; + } if (capture) { @@ -1086,49 +1057,18 @@ void Position::undo_move(Move m) { // Finally point our state pointer back to the previous state st = st->previous; + gamePly--; assert(pos_is_ok()); } -/// Position::do_castle_move() is a private method used to do/undo a castling -/// move. Note that castling moves are encoded as "king captures friendly rook" -/// moves, for instance white short castling in a non-Chess960 game is encoded -/// as e1h1. -template -void Position::do_castle_move(Move m) { +/// Position::do_castle() is a helper used to do/undo a castling move. This +/// is a bit tricky, especially in Chess960. - assert(is_ok(m)); - assert(type_of(m) == CASTLE); - - Square kto, kfrom, rfrom, rto, kAfter, rAfter; +void Position::do_castle(Square kfrom, Square kto, Square rfrom, Square rto) { Color us = sideToMove; - Square kBefore = from_sq(m); - Square rBefore = to_sq(m); - - // Find after-castle squares for king and rook - if (rBefore > kBefore) // O-O - { - kAfter = relative_square(us, SQ_G1); - rAfter = relative_square(us, SQ_F1); - } - else // O-O-O - { - kAfter = relative_square(us, SQ_C1); - rAfter = relative_square(us, SQ_D1); - } - - kfrom = Do ? kBefore : kAfter; - rfrom = Do ? rBefore : rAfter; - - kto = Do ? kAfter : kBefore; - rto = Do ? rAfter : rBefore; - - assert(piece_on(kfrom) == make_piece(us, KING)); - assert(piece_on(rfrom) == make_piece(us, ROOK)); - - // Move the pieces, with some care; in chess960 could be kto == rfrom Bitboard k_from_to_bb = SquareBB[kfrom] ^ SquareBB[kto]; Bitboard r_from_to_bb = SquareBB[rfrom] ^ SquareBB[rto]; byTypeBB[KING] ^= k_from_to_bb; @@ -1136,105 +1076,63 @@ void Position::do_castle_move(Move m) { byTypeBB[ALL_PIECES] ^= k_from_to_bb ^ r_from_to_bb; byColorBB[us] ^= k_from_to_bb ^ r_from_to_bb; - // Update board - Piece king = make_piece(us, KING); - Piece rook = make_piece(us, ROOK); + // Could be from == to, so first set NO_PIECE then KING and ROOK board[kfrom] = board[rfrom] = NO_PIECE; - board[kto] = king; - board[rto] = rook; + board[kto] = make_piece(us, KING); + board[rto] = make_piece(us, ROOK); - // Update piece lists - pieceList[us][KING][index[kfrom]] = kto; - pieceList[us][ROOK][index[rfrom]] = rto; - int tmp = index[rfrom]; // In Chess960 could be kto == rfrom - index[kto] = index[kfrom]; - index[rto] = tmp; - - if (Do) - { - // Reset capture field - st->capturedType = NO_PIECE_TYPE; - - // Update incremental scores - st->psqScore += psq_delta(king, kfrom, kto); - st->psqScore += psq_delta(rook, rfrom, rto); - - // Update hash key - st->key ^= Zobrist::psq[us][KING][kfrom] ^ Zobrist::psq[us][KING][kto]; - st->key ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto]; - - // Clear en passant square - if (st->epSquare != SQ_NONE) - { - st->key ^= Zobrist::enpassant[file_of(st->epSquare)]; - st->epSquare = SQ_NONE; - } - - // Update castling rights - st->key ^= Zobrist::castle[st->castleRights & castleRightsMask[kfrom]]; - st->castleRights &= ~castleRightsMask[kfrom]; - - // Update checkers BB - st->checkersBB = attackers_to(king_square(~us)) & pieces(us); - - sideToMove = ~sideToMove; - } - else - // Undo: point our state pointer back to the previous state - st = st->previous; - - assert(pos_is_ok()); + // Could be kfrom == rto, so use a 'tmp' variable + int tmp = index[kfrom]; + index[rto] = index[rfrom]; + index[kto] = tmp; + pieceList[us][KING][index[kto]] = kto; + pieceList[us][ROOK][index[rto]] = rto; } -/// Position::do_null_move() is used to do/undo a "null move": It flips the side -/// to move and updates the hash key without executing any move on the board. -template -void Position::do_null_move(StateInfo& backupSt) { +/// Position::do(undo)_null_move() is used to do(undo) a "null move": It flips +/// the side to move without executing any move on the board. - assert(!in_check()); +void Position::do_null_move(StateInfo& newSt) { - // Back up the information necessary to undo the null move to the supplied - // StateInfo object. Note that differently from normal case here backupSt - // is actually used as a backup storage not as the new state. This reduces - // the number of fields to be copied. - StateInfo* src = Do ? st : &backupSt; - StateInfo* dst = Do ? &backupSt : st; + assert(!checkers()); - dst->key = src->key; - dst->epSquare = src->epSquare; - dst->psqScore = src->psqScore; - dst->rule50 = src->rule50; - dst->pliesFromNull = src->pliesFromNull; + memcpy(&newSt, st, sizeof(StateInfo)); // Fully copy here + + newSt.previous = st; + st = &newSt; + + if (st->epSquare != SQ_NONE) + { + st->key ^= Zobrist::enpassant[file_of(st->epSquare)]; + st->epSquare = SQ_NONE; + } + + st->key ^= Zobrist::side; + prefetch((char*)TT.first_entry(st->key)); + + st->rule50++; + st->pliesFromNull = 0; sideToMove = ~sideToMove; - if (Do) - { - if (st->epSquare != SQ_NONE) - st->key ^= Zobrist::enpassant[file_of(st->epSquare)]; - - st->key ^= Zobrist::side; - prefetch((char*)TT.first_entry(st->key)); - - st->epSquare = SQ_NONE; - st->rule50++; - st->pliesFromNull = 0; - } - assert(pos_is_ok()); } -// Explicit template instantiations -template void Position::do_null_move(StateInfo& backupSt); -template void Position::do_null_move(StateInfo& backupSt); +void Position::undo_null_move() { + + assert(!checkers()); + + st = st->previous; + sideToMove = ~sideToMove; +} /// Position::see() is a static exchange evaluator: It tries to estimate the -/// material gain or loss resulting from a move. There are three versions of -/// this function: One which takes a destination square as input, one takes a -/// move, and one which takes a 'from' and a 'to' square. The function does -/// not yet understand promotions captures. +/// material gain or loss resulting from a move. Parameter 'asymmThreshold' takes +/// tempi into account. If the side who initiated the capturing sequence does the +/// last capture, he loses a tempo and if the result is below 'asymmThreshold' +/// the capturing sequence is considered bad. int Position::see_sign(Move m) const { @@ -1243,13 +1141,13 @@ int Position::see_sign(Move m) const { // Early return if SEE cannot be negative because captured piece value // is not less then capturing one. Note that king moves always return // here because king midgame value is set to 0. - if (PieceValue[Mg][piece_on(to_sq(m))] >= PieceValue[Mg][piece_moved(m)]) + if (PieceValue[MG][piece_on(to_sq(m))] >= PieceValue[MG][piece_moved(m)]) return 1; return see(m); } -int Position::see(Move m) const { +int Position::see(Move m, int asymmThreshold) const { Square from, to; Bitboard occupied, attackers, stmAttackers; @@ -1290,7 +1188,7 @@ int Position::see(Move m) const { stm = ~color_of(piece_on(from)); stmAttackers = attackers & pieces(stm); if (!stmAttackers) - return PieceValue[Mg][captured]; + return PieceValue[MG][captured]; // The destination square is defended, which makes things rather more // difficult to compute. We proceed by building up a "swap list" containing @@ -1298,14 +1196,14 @@ int Position::see(Move m) const { // destination square, where the sides alternately capture, and always // capture with the least valuable piece. After each capture, we look for // new X-ray attacks from behind the capturing piece. - swapList[0] = PieceValue[Mg][captured]; + swapList[0] = PieceValue[MG][captured]; captured = type_of(piece_on(from)); do { assert(slIndex < 32); // Add the new entry to the swap list - swapList[slIndex] = -swapList[slIndex - 1] + PieceValue[Mg][captured]; + swapList[slIndex] = -swapList[slIndex - 1] + PieceValue[MG][captured]; slIndex++; // Locate and remove from 'occupied' the next least valuable attacker @@ -1326,6 +1224,15 @@ int Position::see(Move m) const { } while (stmAttackers); + // If we are doing asymmetric SEE evaluation and the same side does the first + // and the last capture, he loses a tempo and gain must be at least worth + // 'asymmThreshold', otherwise we replace the score with a very low value, + // before negamaxing. + if (asymmThreshold) + for (int i = 0; i < slIndex; i += 2) + if (swapList[i] < asymmThreshold) + swapList[i] = - QueenValueMg * 16; + // Having built the swap list, we negamax through it to find the best // achievable score from the point of view of the side to move. while (--slIndex) @@ -1347,9 +1254,6 @@ void Position::clear() { for (int i = 0; i < 8; i++) for (int j = 0; j < 16; j++) pieceList[0][i][j] = pieceList[1][i][j] = SQ_NONE; - - for (Square sq = SQ_A1; sq <= SQ_H8; sq++) - board[sq] = NO_PIECE; } @@ -1463,7 +1367,7 @@ Value Position::compute_non_pawn_material(Color c) const { Value value = VALUE_ZERO; for (PieceType pt = KNIGHT; pt <= QUEEN; pt++) - value += piece_count(c, pt) * PieceValue[Mg][pt]; + value += piece_count(c, pt) * PieceValue[MG][pt]; return value; } @@ -1472,7 +1376,6 @@ Value Position::compute_non_pawn_material(Color c) const { /// Position::is_draw() tests whether the position is drawn by material, /// repetition, or the 50 moves rule. It does not detect stalemates, this /// must be done by the search. -template bool Position::is_draw() const { // Draw by material? @@ -1481,37 +1384,30 @@ bool Position::is_draw() const { return true; // Draw by the 50 moves rule? - if (st->rule50 > 99 && (!in_check() || MoveList(*this).size())) + if (st->rule50 > 99 && (!checkers() || MoveList(*this).size())) return true; // Draw by repetition? - if (!SkipRepetition) + int i = 4, e = std::min(st->rule50, st->pliesFromNull); + + if (i <= e) { - int i = 4, e = std::min(st->rule50, st->pliesFromNull); + StateInfo* stp = st->previous->previous; - if (i <= e) - { - StateInfo* stp = st->previous->previous; + do { + stp = stp->previous->previous; - do { - stp = stp->previous->previous; + if (stp->key == st->key) + return true; - if (stp->key == st->key) - return true; + i += 2; - i +=2; - - } while (i <= e); - } + } while (i <= e); } return false; } -// Explicit template instantiations -template bool Position::is_draw() const; -template bool Position::is_draw() const; - /// Position::flip() flips position with the white and black sides reversed. This /// is only useful for debugging especially for finding evaluation symmetry bugs. @@ -1526,7 +1422,7 @@ void Position::flip() { thisThread = pos.this_thread(); nodes = pos.nodes_searched(); chess960 = pos.is_chess960(); - startPosPly = pos.startpos_ply_counter(); + gamePly = pos.game_ply(); for (Square s = SQ_A1; s <= SQ_H8; s++) if (!pos.is_empty(s)) @@ -1593,7 +1489,7 @@ bool Position::pos_is_ok(int* failedStep) const { if ((*step)++, debugKingCount) { - int kingCount[2] = {}; + int kingCount[COLOR_NB] = {}; for (Square s = SQ_A1; s <= SQ_H8; s++) if (type_of(piece_on(s)) == KING) diff --git a/DroidFish/jni/stockfish/position.h b/DroidFish/jni/stockfish/position.h index 7418b0d..7616ead 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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,6 +21,7 @@ #define POSITION_H_INCLUDED #include +#include #include "bitboard.h" #include "types.h" @@ -29,7 +30,7 @@ /// The checkInfo struct is initialized at c'tor time and keeps info used /// to detect if a move gives check. class Position; -class Thread; +struct Thread; struct CheckInfo { @@ -37,19 +38,19 @@ struct CheckInfo { Bitboard dcCandidates; Bitboard pinned; - Bitboard checkSq[8]; + Bitboard checkSq[PIECE_TYPE_NB]; Square ksq; }; /// The StateInfo struct stores information we need 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), an StateInfo object +/// is made on the board (by calling Position::do_move), a StateInfo object /// must be passed as a parameter. struct StateInfo { Key pawnKey, materialKey; - Value npMaterial[2]; + Value npMaterial[COLOR_NB]; int castleRights, rule50, pliesFromNull; Score psqScore; Square epSquare; @@ -60,13 +61,10 @@ struct StateInfo { StateInfo* previous; }; -struct ReducedStateInfo { - Key pawnKey, materialKey; - Value npMaterial[2]; - int castleRights, rule50, pliesFromNull; - Score psqScore; - Square epSquare; -}; + +/// 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. +const size_t StateCopySize64 = offsetof(StateInfo, key) / sizeof(uint64_t) + 1; /// The position data structure. A position consists of the following data: @@ -95,13 +93,13 @@ class Position { public: Position() {} Position(const Position& p, Thread* t) { *this = p; thisThread = t; } - Position(const std::string& f, bool c960, Thread* t) { from_fen(f, c960, t); } + Position(const std::string& f, bool c960, Thread* t) { set(f, c960, t); } Position& operator=(const Position&); // Text input/output - void from_fen(const std::string& fen, bool isChess960, Thread* th); - const std::string to_fen() const; - void print(Move m = MOVE_NONE) const; + void set(const std::string& fen, bool isChess960, Thread* th); + const std::string fen() const; + const std::string pretty(Move m = MOVE_NONE) const; // Position representation Bitboard pieces() const; @@ -124,7 +122,6 @@ public: Square castle_rook_square(Color c, CastlingSide s) const; // Checking - bool in_check() const; Bitboard checkers() const; Bitboard discovered_check_candidates() const; Bitboard pinned_pieces() const; @@ -139,8 +136,6 @@ public: // Properties of moves bool move_gives_check(Move m, const CheckInfo& ci) const; - bool move_attacks_square(Move m, Square s) const; - bool move_is_legal(const Move m) const; bool pl_move_is_legal(Move m, Bitboard pinned) const; bool is_pseudo_legal(const Move m) const; bool is_capture(Move m) const; @@ -159,10 +154,11 @@ public: void do_move(Move m, StateInfo& st); void do_move(Move m, StateInfo& st, const CheckInfo& ci, bool moveIsCheck); void undo_move(Move m); - template void do_null_move(StateInfo& st); + void do_null_move(StateInfo& st); + void undo_null_move(); // Static exchange evaluation - int see(Move m) const; + int see(Move m, int asymmThreshold = 0) const; int see_sign(Move m) const; // Accessing hash keys @@ -178,12 +174,12 @@ public: // Other properties of the position Color side_to_move() const; - int startpos_ply_counter() const; + int game_ply() const; bool is_chess960() const; Thread* this_thread() const; int64_t nodes_searched() const; void set_nodes_searched(int64_t n); - template bool is_draw() const; + bool is_draw() const; // Position consistency check, for debugging bool pos_is_ok(int* failedStep = NULL) const; @@ -195,8 +191,8 @@ private: void put_piece(Piece p, Square s); void set_castle_right(Color c, Square rfrom); - // Helper template functions - template void do_castle_move(Move m); + // Helper functions + void do_castle(Square kfrom, Square kto, Square rfrom, Square rto); template Bitboard hidden_checkers() const; // Computing hash keys from scratch (for initialization and debugging) @@ -209,20 +205,20 @@ private: Value compute_non_pawn_material(Color c) const; // Board and pieces - Piece board[64]; // [square] - Bitboard byTypeBB[8]; // [pieceType] - Bitboard byColorBB[2]; // [color] - int pieceCount[2][8]; // [color][pieceType] - Square pieceList[2][8][16]; // [color][pieceType][index] - int index[64]; // [square] + 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 castleRightsMask[64]; // [square] - Square castleRookSquare[2][2]; // [color][side] - Bitboard castlePath[2][2]; // [color][side] + int castleRightsMask[SQUARE_NB]; + Square castleRookSquare[COLOR_NB][CASTLING_SIDE_NB]; + Bitboard castlePath[COLOR_NB][CASTLING_SIDE_NB]; StateInfo startState; int64_t nodes; - int startPosPly; + int gamePly; Color sideToMove; Thread* thisThread; StateInfo* st; @@ -334,10 +330,6 @@ inline Bitboard Position::checkers() const { return st->checkersBB; } -inline bool Position::in_check() const { - return st->checkersBB != 0; -} - inline Bitboard Position::discovered_check_candidates() const { return hidden_checkers(); } @@ -384,8 +376,8 @@ inline bool Position::is_passed_pawn_push(Move m) const { && pawn_is_passed(sideToMove, to_sq(m)); } -inline int Position::startpos_ply_counter() const { - return startPosPly + st->pliesFromNull; // HACK +inline int Position::game_ply() const { + return gamePly; } inline bool Position::opposite_bishops() const { diff --git a/DroidFish/jni/stockfish/psqtab.h b/DroidFish/jni/stockfish/psqtab.h index a2fb661..ca39fe8 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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,7 +29,7 @@ /// a given square a (midgame, endgame) score pair is assigned. PSQT is defined /// for white side, for black side the tables are symmetric. -static const Score PSQT[][64] = { +static const Score PSQT[][SQUARE_NB] = { { }, { // Pawn S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0), diff --git a/DroidFish/jni/stockfish/rkiss.h b/DroidFish/jni/stockfish/rkiss.h index 154f3ca..47a3d4c 100644 --- a/DroidFish/jni/stockfish/rkiss.h +++ b/DroidFish/jni/stockfish/rkiss.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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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 @@ -61,17 +61,15 @@ class RKISS { return s.d = e + s.a; } - // Init seed and scramble a few rounds - void raninit() { +public: + RKISS(int seed = 73) { s.a = 0xf1ea5eed; s.b = s.c = s.d = 0xd4e12c77; - for (int i = 0; i < 73; i++) + for (int i = 0; i < seed; i++) // Scramble a few rounds rand64(); } -public: - RKISS() { raninit(); } template T rand() { return T(rand64()); } }; diff --git a/DroidFish/jni/stockfish/search.cpp b/DroidFish/jni/stockfish/search.cpp index 6294859..dfd2d3b 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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 @@ -26,7 +26,6 @@ #include "book.h" #include "evaluate.h" -#include "history.h" #include "movegen.h" #include "movepick.h" #include "notation.h" @@ -41,7 +40,8 @@ namespace Search { volatile SignalsType Signals; LimitsType Limits; std::vector RootMoves; - Position RootPosition; + Position RootPos; + Color RootColor; Time::point SearchTime; StateStackPtr SetupStates; } @@ -55,35 +55,15 @@ namespace { // Set to true to force running with one thread. Used for debugging const bool FakeSplit = false; + // This is the minimum interval in msec between two check_time() calls + const int TimerResolution = 5; + // Different node types, used as template parameter enum NodeType { Root, PV, NonPV, SplitPointRoot, SplitPointPV, SplitPointNonPV }; - // Lookup table to check if a Piece is a slider and its access function - const bool Slidings[18] = { 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1 }; - inline bool piece_is_slider(Piece p) { return Slidings[p]; } - - // Maximum depth for razoring - const Depth RazorDepth = 4 * ONE_PLY; - // Dynamic razoring margin based on depth inline Value razor_margin(Depth d) { return Value(512 + 16 * int(d)); } - // Maximum depth for use of dynamic threat detection when null move fails low - const Depth ThreatDepth = 5 * ONE_PLY; - - // Minimum depth for use of internal iterative deepening - const Depth IIDDepth[] = { 8 * ONE_PLY, 5 * ONE_PLY }; - - // At Non-PV nodes we do an internal iterative deepening search - // when the static evaluation is bigger then beta - IIDMargin. - const Value IIDMargin = Value(256); - - // Minimum depth for use of singular extension - const Depth SingularExtensionDepth[] = { 8 * ONE_PLY, 6 * ONE_PLY }; - - // Futility margin for quiescence search - const Value FutilityMarginQS = Value(128); - // Futility lookup tables (initialized at startup) and their access functions Value FutilityMargins[16][64]; // [depth][moveNumber] int FutilityMoveCounts[32]; // [depth] @@ -94,11 +74,6 @@ namespace { : 2 * VALUE_INFINITE; } - inline int futility_move_count(Depth d) { - - return d < 16 * ONE_PLY ? FutilityMoveCounts[d] : MAX_MOVES; - } - // Reduction lookup tables (initialized at startup) and their access function int8_t Reductions[2][64][64]; // [pv][depth][moveNumber] @@ -107,62 +82,42 @@ namespace { return (Depth) Reductions[PvNode][std::min(int(d) / ONE_PLY, 63)][std::min(mn, 63)]; } - // Easy move margin. An easy move candidate must be at least this much better - // than the second best move. - const Value EasyMoveMargin = Value(0x150); - - // This is the minimum interval in msec between two check_time() calls - const int TimerResolution = 5; - - - size_t MultiPV, UCIMultiPV, PVIdx; + size_t PVSize, PVIdx; TimeManager TimeMgr; int BestMoveChanges; - int SkillLevel; - bool SkillLevelEnabled, Chess960; - History H; - + Value DrawValue[COLOR_NB]; + History Hist; + Gains Gain; template Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth); - template + template Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth); void id_loop(Position& pos); - bool check_is_dangerous(Position &pos, Move move, Value futilityBase, Value beta); - bool connected_moves(const Position& pos, Move m1, Move m2); Value value_to_tt(Value v, int ply); Value value_from_tt(Value v, int ply); - bool can_return_tt(const TTEntry* tte, Depth depth, Value ttValue, Value beta); - bool connected_threat(const Position& pos, Move m, Move threat); - Value refine_eval(const TTEntry* tte, Value ttValue, Value defaultEval); - Move do_skill_level(); + bool check_is_dangerous(const Position& pos, Move move, Value futilityBase, Value beta); + bool allows(const Position& pos, Move first, Move second); + bool refutes(const Position& pos, Move first, Move second); string uci_pv(const Position& pos, int depth, Value alpha, Value beta); - // is_dangerous() checks whether a move belongs to some classes of known - // 'dangerous' moves so that we avoid to prune it. - FORCE_INLINE bool is_dangerous(const Position& pos, Move m, bool captureOrPromotion) { + struct Skill { + Skill(int l) : level(l), best(MOVE_NONE) {} + ~Skill() { + if (enabled()) // Swap best PV line with the sub-optimal one + std::swap(RootMoves[0], *std::find(RootMoves.begin(), + RootMoves.end(), best ? best : pick_move())); + } - // Castle move? - if (type_of(m) == CASTLE) - return true; + bool enabled() const { return level < 20; } + bool time_to_pick(int depth) const { return depth == 1 + level; } + Move pick_move(); - // Passed pawn move? - if ( type_of(pos.piece_moved(m)) == PAWN - && pos.pawn_is_passed(pos.side_to_move(), to_sq(m))) - return true; - - // Entering a pawn endgame? - if ( captureOrPromotion - && type_of(pos.piece_on(to_sq(m))) != PAWN - && type_of(m) == NORMAL - && ( pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) - - PieceValue[Mg][pos.piece_on(to_sq(m))] == VALUE_ZERO)) - return true; - - return false; - } + int level; + Move best; + }; } // namespace @@ -190,7 +145,7 @@ void Search::init() { // Init futility move count array for (d = 0; d < 32; d++) - FutilityMoveCounts[d] = int(3.001 + 0.25 * pow(d, 2.0)); + FutilityMoveCounts[d] = int(3.001 + 0.25 * pow(double(d), 2.0)); } @@ -220,31 +175,28 @@ size_t Search::perft(Position& pos, Depth depth) { /// Search::think() is the external interface to Stockfish's search, and is /// called by the main thread when the program receives the UCI 'go' command. It -/// searches from RootPosition and at the end prints the "bestmove" to output. +/// searches from RootPos and at the end prints the "bestmove" to output. void Search::think() { static PolyglotBook book; // Defined static to initialize the PRNG only once - Position& pos = RootPosition; - Chess960 = pos.is_chess960(); - Eval::RootColor = pos.side_to_move(); - TimeMgr.init(Limits, pos.startpos_ply_counter(), pos.side_to_move()); - TT.new_search(); - H.clear(); + RootColor = RootPos.side_to_move(); + TimeMgr.init(Limits, RootPos.game_ply(), RootColor); if (RootMoves.empty()) { - sync_cout << "info depth 0 score " - << score_to_uci(pos.in_check() ? -VALUE_MATE : VALUE_DRAW) << sync_endl; - RootMoves.push_back(MOVE_NONE); + sync_cout << "info depth 0 score " + << score_to_uci(RootPos.checkers() ? -VALUE_MATE : VALUE_DRAW) + << sync_endl; + goto finalize; } - if (Options["OwnBook"] && !Limits.infinite) + if (Options["OwnBook"] && !Limits.infinite && !Limits.mate) { - Move bookMove = book.probe(pos, Options["Book File"], Options["Best Book Move"]); + Move bookMove = book.probe(RootPos, Options["Book File"], Options["Best Book Move"]); if (bookMove && std::count(RootMoves.begin(), RootMoves.end(), bookMove)) { @@ -253,67 +205,80 @@ void Search::think() { } } - UCIMultiPV = Options["MultiPV"]; - SkillLevel = Options["Skill Level"]; - - // Do we have to play with skill handicap? In this case enable MultiPV that - // we will use behind the scenes to retrieve a set of possible moves. - SkillLevelEnabled = (SkillLevel < 20); - MultiPV = (SkillLevelEnabled ? std::max(UCIMultiPV, (size_t)4) : UCIMultiPV); + if (Options["Contempt Factor"] && !Options["UCI_AnalyseMode"]) + { + int cf = Options["Contempt Factor"] * PawnValueMg / 100; // From centipawns + cf = cf * Material::game_phase(RootPos) / PHASE_MIDGAME; // Scale down with phase + DrawValue[ RootColor] = VALUE_DRAW - Value(cf); + DrawValue[~RootColor] = VALUE_DRAW + Value(cf); + } + else + DrawValue[WHITE] = DrawValue[BLACK] = VALUE_DRAW; if (Options["Use Search Log"]) { Log log(Options["Search Log Filename"]); - log << "\nSearching: " << pos.to_fen() + log << "\nSearching: " << RootPos.fen() << "\ninfinite: " << Limits.infinite << " ponder: " << Limits.ponder - << " time: " << Limits.time[pos.side_to_move()] - << " increment: " << Limits.inc[pos.side_to_move()] + << " time: " << Limits.time[RootColor] + << " increment: " << Limits.inc[RootColor] << " moves to go: " << Limits.movestogo << std::endl; } - Threads.wake_up(); + // Reset the threads, still sleeping: will be wake up at split time + for (size_t i = 0; i < Threads.size(); i++) + Threads[i]->maxPly = 0; + + Threads.sleepWhileIdle = Options["Use Sleeping Threads"]; // Set best timer interval to avoid lagging under time pressure. Timer is // used to check for remaining available thinking time. - if (Limits.use_time_management()) - Threads.set_timer(std::min(100, std::max(TimeMgr.available_time() / 16, TimerResolution))); - else - Threads.set_timer(100); + Threads.timer->msec = + Limits.use_time_management() ? std::min(100, std::max(TimeMgr.available_time() / 16, TimerResolution)) : + Limits.nodes ? 2 * TimerResolution + : 100; - // We're ready to start searching. Call the iterative deepening loop function - id_loop(pos); + Threads.timer->notify_one(); // Wake up the recurring timer - Threads.set_timer(0); // Stop timer - Threads.sleep(); + id_loop(RootPos); // Let's start searching ! + + Threads.timer->msec = 0; // Stop the timer + Threads.sleepWhileIdle = true; // Send idle threads to sleep if (Options["Use Search Log"]) { Time::point elapsed = Time::now() - SearchTime + 1; Log log(Options["Search Log Filename"]); - log << "Nodes: " << pos.nodes_searched() - << "\nNodes/second: " << pos.nodes_searched() * 1000 / elapsed - << "\nBest move: " << move_to_san(pos, RootMoves[0].pv[0]); + log << "Nodes: " << RootPos.nodes_searched() + << "\nNodes/second: " << RootPos.nodes_searched() * 1000 / elapsed + << "\nBest move: " << move_to_san(RootPos, RootMoves[0].pv[0]); StateInfo st; - pos.do_move(RootMoves[0].pv[0], st); - log << "\nPonder move: " << move_to_san(pos, RootMoves[0].pv[1]) << std::endl; - pos.undo_move(RootMoves[0].pv[0]); + RootPos.do_move(RootMoves[0].pv[0], st); + log << "\nPonder move: " << move_to_san(RootPos, RootMoves[0].pv[1]) << std::endl; + RootPos.undo_move(RootMoves[0].pv[0]); } finalize: // When we reach max depth we arrive here even without Signals.stop is raised, - // but if we are pondering or in infinite search, we shouldn't print the best - // move before we are told to do so. + // but if we are pondering or in infinite search, according to UCI protocol, + // we shouldn't print the best move before the GUI sends a "stop" or "ponderhit" + // command. We simply wait here until GUI sends one of those commands (that + // raise Signals.stop). if (!Signals.stop && (Limits.ponder || Limits.infinite)) - pos.this_thread()->wait_for_stop_or_ponderhit(); + { + Signals.stopOnPonderhit = true; + 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], Chess960) - << " ponder " << move_to_uci(RootMoves[0].pv[1], Chess960) << sync_endl; + 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; } @@ -328,27 +293,38 @@ namespace { Stack ss[MAX_PLY_PLUS_2]; int depth, prevBestMoveChanges; Value bestValue, alpha, beta, delta; - bool bestMoveNeverChanged = true; - Move skillBest = MOVE_NONE; memset(ss, 0, 4 * sizeof(Stack)); depth = BestMoveChanges = 0; bestValue = delta = -VALUE_INFINITE; ss->currentMove = MOVE_NULL; // Hack to skip update gains + TT.new_search(); + Hist.clear(); + Gain.clear(); + + PVSize = Options["MultiPV"]; + Skill skill(Options["Skill Level"]); + + // Do we have to play with skill handicap? In this case enable MultiPV search + // that we will use behind the scenes to retrieve a set of possible moves. + if (skill.enabled() && PVSize < 4) + PVSize = 4; + + PVSize = std::min(PVSize, RootMoves.size()); // Iterative deepening loop until requested to stop or target depth reached - while (!Signals.stop && ++depth <= MAX_PLY && (!Limits.depth || depth <= Limits.depth)) + while (++depth <= MAX_PLY && !Signals.stop && (!Limits.depth || depth <= Limits.depth)) { // Save last iteration's scores before first PV line is searched and all // the move scores but the (new) PV are set to -VALUE_INFINITE. for (size_t i = 0; i < RootMoves.size(); i++) RootMoves[i].prevScore = RootMoves[i].score; - prevBestMoveChanges = BestMoveChanges; + prevBestMoveChanges = BestMoveChanges; // Only sensible when PVSize == 1 BestMoveChanges = 0; // MultiPV loop. We perform a full root search for each PV line - for (PVIdx = 0; PVIdx < std::min(MultiPV, RootMoves.size()); PVIdx++) + for (PVIdx = 0; PVIdx < PVSize; PVIdx++) { // Set aspiration window default width if (depth >= 5 && abs(RootMoves[PVIdx].prevScore) < VALUE_KNOWN_WIN) @@ -377,39 +353,39 @@ namespace { // we want to keep the same order for all the moves but the new // PV that goes to the front. Note that in case of MultiPV search // the already searched PV lines are preserved. - sort(RootMoves.begin() + PVIdx, RootMoves.end()); - - // In case we have found an exact score and we are going to leave - // the fail high/low loop then reorder the PV moves, otherwise - // leave the last PV move in its position so to be searched again. - // Of course this is needed only in MultiPV search. - if (PVIdx && bestValue > alpha && bestValue < beta) - sort(RootMoves.begin(), RootMoves.begin() + PVIdx); + std::stable_sort(RootMoves.begin() + PVIdx, RootMoves.end()); // Write PV back to transposition table in case the relevant // entries have been overwritten during the search. for (size_t i = 0; i <= PVIdx; i++) RootMoves[i].insert_pv_in_tt(pos); - // If search has been stopped exit the aspiration window loop. - // Sorting and writing PV back to TT is safe becuase RootMoves - // is still valid, although refers to previous iteration. + // If search has been stopped return immediately. Sorting and + // writing PV back to TT is safe becuase RootMoves is still + // valid, although refers to previous iteration. if (Signals.stop) - break; - - // Send full PV info to GUI if we are going to leave the loop or - // if we have a fail high/low and we are deep in the search. - if ((bestValue > alpha && bestValue < beta) || Time::now() - SearchTime > 2000) - sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl; + return; // In case of failing high/low increase aspiration window and - // research, otherwise exit the fail high/low loop. - if (bestValue >= beta) + // research, otherwise exit the loop. + if (bestValue > alpha && bestValue < beta) + break; + + // Give some update (without cluttering the UI) before to research + if (Time::now() - SearchTime > 3000) + sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl; + + if (abs(bestValue) >= VALUE_KNOWN_WIN) + { + alpha = -VALUE_INFINITE; + beta = VALUE_INFINITE; + } + else if (bestValue >= beta) { beta += delta; delta += delta / 2; } - else if (bestValue <= alpha) + else { Signals.failedLowAtRoot = true; Signals.stopOnPonderhit = false; @@ -417,42 +393,41 @@ namespace { alpha -= delta; delta += delta / 2; } - else - break; - - // Search with full window in case we have a win/mate score - if (abs(bestValue) >= VALUE_KNOWN_WIN) - { - alpha = -VALUE_INFINITE; - beta = VALUE_INFINITE; - } assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE); } + + // Sort the PV lines searched so far and update the GUI + std::stable_sort(RootMoves.begin(), RootMoves.begin() + PVIdx + 1); + + if (PVIdx + 1 == PVSize || Time::now() - SearchTime > 3000) + sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl; } - // Skills: Do we need to pick now the best move ? - if (SkillLevelEnabled && depth == 1 + SkillLevel) - skillBest = do_skill_level(); + // Do we need to pick now the sub-optimal best move ? + if (skill.enabled() && skill.time_to_pick(depth)) + skill.pick_move(); - if (!Signals.stop && Options["Use Search Log"]) + if (Options["Use Search Log"]) { Log log(Options["Search Log Filename"]); log << pretty_pv(pos, depth, bestValue, Time::now() - SearchTime, &RootMoves[0].pv[0]) << std::endl; } - // Filter out startup noise when monitoring best move stability - if (depth > 2 && BestMoveChanges) - bestMoveNeverChanged = false; + // Do we have found a "mate in x"? + if ( Limits.mate + && bestValue >= VALUE_MATE_IN_MAX_PLY + && VALUE_MATE - bestValue <= 2 * Limits.mate) + Signals.stop = true; // Do we have time for the next iteration? Can we stop searching now? - if (!Signals.stop && !Signals.stopOnPonderhit && Limits.use_time_management()) + if (Limits.use_time_management() && !Signals.stopOnPonderhit) { bool stop = false; // Local variable, not the volatile Signals.stop // Take in account some extra time if the best move has changed - if (depth > 4 && depth < 50) + if (depth > 4 && depth < 50 && PVSize == 1) TimeMgr.pv_instability(BestMoveChanges, prevBestMoveChanges); // Stop search if most of available time is already consumed. We @@ -464,10 +439,12 @@ namespace { // Stop search early if one move seems to be much better than others if ( depth >= 12 && !stop - && ( (bestMoveNeverChanged && pos.captured_piece_type()) - || Time::now() - SearchTime > (TimeMgr.available_time() * 40) / 100)) + && PVSize == 1 + && bestValue > VALUE_MATED_IN_MAX_PLY + && ( RootMoves.size() == 1 + || Time::now() - SearchTime > (TimeMgr.available_time() * 20) / 100)) { - Value rBeta = bestValue - EasyMoveMargin; + Value rBeta = bestValue - 2 * PawnValueMg; (ss+1)->excludedMove = RootMoves[0].pv[0]; (ss+1)->skipNullMove = true; Value v = search(pos, ss+1, rBeta - 1, rBeta, (depth - 3) * ONE_PLY); @@ -489,15 +466,6 @@ namespace { } } } - - // When using skills swap best PV line with the sub-optimal one - if (SkillLevelEnabled) - { - if (skillBest == MOVE_NONE) // Still unassigned ? - skillBest = do_skill_level(); - - std::swap(RootMoves[0], *std::find(RootMoves.begin(), RootMoves.end(), skillBest)); - } } @@ -516,75 +484,65 @@ namespace { const bool RootNode = (NT == Root || NT == SplitPointRoot); assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE); - assert((alpha == beta - 1) || PvNode); + assert(PvNode || (alpha == beta - 1)); assert(depth > DEPTH_ZERO); Move movesSearched[64]; StateInfo st; const TTEntry *tte; + SplitPoint* splitPoint; Key posKey; Move ttMove, move, excludedMove, bestMove, threatMove; Depth ext, newDepth; - Bound bt; - Value bestValue, value, oldAlpha, ttValue; - Value refinedValue, nullValue, futilityBase, futilityValue; - bool isPvMove, inCheck, singularExtensionNode, givesCheck; + Value bestValue, value, ttValue; + Value eval, nullValue, futilityValue; + bool inCheck, givesCheck, pvMove, singularExtensionNode; bool captureOrPromotion, dangerous, doFullDepthSearch; - int moveCount = 0, playedMoveCount = 0; - Thread* thisThread = pos.this_thread(); - SplitPoint* sp = NULL; + int moveCount, playedMoveCount; - refinedValue = bestValue = value = -VALUE_INFINITE; - oldAlpha = alpha; - inCheck = pos.in_check(); + // Step 1. Initialize node + Thread* thisThread = pos.this_thread(); + moveCount = playedMoveCount = 0; + inCheck = pos.checkers(); + + if (SpNode) + { + splitPoint = ss->splitPoint; + bestMove = splitPoint->bestMove; + threatMove = splitPoint->threatMove; + bestValue = splitPoint->bestValue; + tte = NULL; + ttMove = excludedMove = MOVE_NONE; + ttValue = VALUE_NONE; + + assert(splitPoint->bestValue > -VALUE_INFINITE && splitPoint->moveCount > 0); + + goto split_point_start; + } + + bestValue = -VALUE_INFINITE; + ss->currentMove = threatMove = (ss+1)->excludedMove = bestMove = MOVE_NONE; ss->ply = (ss-1)->ply + 1; + ss->futilityMoveCount = 0; + (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) thisThread->maxPly = ss->ply; - // Step 1. Initialize node - if (SpNode) - { - tte = NULL; - ttMove = excludedMove = MOVE_NONE; - ttValue = VALUE_ZERO; - sp = ss->sp; - bestMove = sp->bestMove; - threatMove = sp->threatMove; - bestValue = sp->bestValue; - moveCount = sp->moveCount; // Lock must be held here - - assert(bestValue > -VALUE_INFINITE && moveCount > 0); - - goto split_point_start; - } - else - { - ss->currentMove = threatMove = (ss+1)->excludedMove = bestMove = MOVE_NONE; - (ss+1)->skipNullMove = false; (ss+1)->reduction = DEPTH_ZERO; - (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; - - } - - // Step 2. Check for aborted search and immediate draw - // Enforce node limit here. FIXME: This only works with 1 search thread. - if (Limits.nodes && pos.nodes_searched() >= Limits.nodes) - Signals.stop = true; - - if (( Signals.stop - || pos.is_draw() - || ss->ply > MAX_PLY) && !RootNode) - return VALUE_DRAW; - - // 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 - // a shorter mate was found upward in the tree then there is no need to search - // further, we will never beat current alpha. Same logic but with reversed signs - // applies also in the opposite condition of being mated instead of giving mate, - // in this case return a fail-high score. if (!RootNode) { + // Step 2. Check for aborted search and immediate draw + if (Signals.stop || pos.is_draw() || ss->ply > MAX_PLY) + return 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 + // a shorter mate was found upward in the tree then there is no need to search + // further, we will never beat current alpha. Same logic but with reversed signs + // applies also in the opposite condition of being mated instead of giving mate, + // in this case return a fail-high score. alpha = std::max(mated_in(ss->ply), alpha); beta = std::min(mate_in(ss->ply+1), beta); if (alpha >= beta) @@ -598,14 +556,19 @@ namespace { posKey = excludedMove ? pos.exclusion_key() : pos.key(); tte = TT.probe(posKey); ttMove = RootNode ? RootMoves[PVIdx].pv[0] : tte ? tte->move() : MOVE_NONE; - ttValue = tte ? value_from_tt(tte->value(), ss->ply) : VALUE_ZERO; + ttValue = tte ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE; // At PV nodes we check for exact scores, while at non-PV nodes we check for // a fail high/low. Biggest advantage at 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 && (PvNode ? tte->depth() >= depth && tte->type() == BOUND_EXACT - : can_return_tt(tte, depth, ttValue, beta))) + if ( !RootNode + && tte + && tte->depth() >= depth + && ttValue != VALUE_NONE // Only in case of TT access race + && ( PvNode ? tte->type() == BOUND_EXACT + : ttValue >= beta ? (tte->type() & BOUND_LOWER) + : (tte->type() & BOUND_UPPER))) { TT.refresh(tte); ss->currentMove = ttMove; // Can be MOVE_NONE @@ -623,44 +586,51 @@ namespace { // Step 5. Evaluate the position statically and update parent's gain statistics if (inCheck) - ss->eval = ss->evalMargin = VALUE_NONE; + ss->staticEval = ss->evalMargin = eval = VALUE_NONE; + else if (tte) { - assert(tte->static_value() != VALUE_NONE); + // Never assume anything on values stored in TT + if ( (ss->staticEval = eval = tte->eval_value()) == VALUE_NONE + ||(ss->evalMargin = tte->eval_margin()) == VALUE_NONE) + eval = ss->staticEval = evaluate(pos, ss->evalMargin); - ss->eval = tte->static_value(); - ss->evalMargin = tte->static_value_margin(); - refinedValue = refine_eval(tte, ttValue, ss->eval); + // Can ttValue be used as a better position evaluation? + if (ttValue != VALUE_NONE) + if ( ((tte->type() & BOUND_LOWER) && ttValue > eval) + || ((tte->type() & BOUND_UPPER) && ttValue < eval)) + eval = ttValue; } else { - refinedValue = ss->eval = evaluate(pos, ss->evalMargin); - TT.store(posKey, VALUE_NONE, BOUND_NONE, DEPTH_NONE, MOVE_NONE, ss->eval, ss->evalMargin); + eval = ss->staticEval = evaluate(pos, ss->evalMargin); + TT.store(posKey, VALUE_NONE, BOUND_NONE, DEPTH_NONE, MOVE_NONE, + ss->staticEval, ss->evalMargin); } // Update gain for the parent non-capture move given the static position // evaluation before and after the move. - if ( (move = (ss-1)->currentMove) != MOVE_NULL - && (ss-1)->eval != VALUE_NONE - && ss->eval != VALUE_NONE + if ( (move = (ss-1)->currentMove) != MOVE_NULL + && (ss-1)->staticEval != VALUE_NONE + && ss->staticEval != VALUE_NONE && !pos.captured_piece_type() && type_of(move) == NORMAL) { Square to = to_sq(move); - H.update_gain(pos.piece_on(to), to, -(ss-1)->eval - ss->eval); + Gain.update(pos.piece_on(to), to, -(ss-1)->staticEval - ss->staticEval); } // Step 6. Razoring (is omitted in PV nodes) if ( !PvNode - && depth < RazorDepth + && depth < 4 * ONE_PLY && !inCheck - && refinedValue + razor_margin(depth) < beta + && eval + razor_margin(depth) < beta && ttMove == MOVE_NONE && abs(beta) < VALUE_MATE_IN_MAX_PLY && !pos.pawn_on_7th(pos.side_to_move())) { Value rbeta = beta - razor_margin(depth); - Value v = qsearch(pos, ss, rbeta-1, rbeta, DEPTH_ZERO); + Value v = qsearch(pos, ss, rbeta-1, rbeta, DEPTH_ZERO); if (v < rbeta) // Logically we should return (v + razor_margin(depth)), but // surprisingly this did slightly weaker in tests. @@ -672,19 +642,20 @@ namespace { // the score by more than futility_margin(depth) if we do a null move. if ( !PvNode && !ss->skipNullMove - && depth < RazorDepth + && depth < 4 * ONE_PLY && !inCheck - && refinedValue - futility_margin(depth, 0) >= beta + && eval - futility_margin(depth, (ss-1)->futilityMoveCount) >= beta && abs(beta) < VALUE_MATE_IN_MAX_PLY + && abs(eval) < VALUE_KNOWN_WIN && pos.non_pawn_material(pos.side_to_move())) - return refinedValue - futility_margin(depth, 0); + return eval - futility_margin(depth, (ss-1)->futilityMoveCount); // Step 8. Null move search with verification search (is omitted in PV nodes) if ( !PvNode && !ss->skipNullMove && depth > ONE_PLY && !inCheck - && refinedValue >= beta + && eval >= beta && abs(beta) < VALUE_MATE_IN_MAX_PLY && pos.non_pawn_material(pos.side_to_move())) { @@ -694,15 +665,15 @@ namespace { Depth R = 3 * ONE_PLY + depth / 4; // Null move dynamic reduction based on value - if (refinedValue - PawnValueMg > beta) + if (eval - PawnValueMg > beta) R += ONE_PLY; - pos.do_null_move(st); + pos.do_null_move(st); (ss+1)->skipNullMove = true; - nullValue = depth-R < ONE_PLY ? -qsearch(pos, ss+1, -beta, -alpha, DEPTH_ZERO) + nullValue = depth-R < ONE_PLY ? -qsearch(pos, ss+1, -beta, -alpha, DEPTH_ZERO) : - search(pos, ss+1, -beta, -alpha, depth-R); (ss+1)->skipNullMove = false; - pos.do_null_move(st); + pos.undo_null_move(); if (nullValue >= beta) { @@ -710,7 +681,7 @@ namespace { if (nullValue >= VALUE_MATE_IN_MAX_PLY) nullValue = beta; - if (depth < 6 * ONE_PLY) + if (depth < 12 * ONE_PLY) return nullValue; // Do verification search at high depths @@ -731,10 +702,10 @@ namespace { // parent node, which will trigger a re-search with full depth). threatMove = (ss+1)->currentMove; - if ( depth < ThreatDepth + if ( depth < 5 * ONE_PLY && (ss-1)->reduction && threatMove != MOVE_NONE - && connected_moves(pos, (ss-1)->currentMove, threatMove)) + && allows(pos, (ss-1)->currentMove, threatMove)) return beta - 1; } } @@ -744,7 +715,7 @@ namespace { // and a reduced search returns a value much above beta, we can (almost) safely // prune the previous move. if ( !PvNode - && depth >= RazorDepth + ONE_PLY + && depth >= 5 * ONE_PLY && !inCheck && !ss->skipNullMove && excludedMove == MOVE_NONE @@ -757,7 +728,7 @@ namespace { assert((ss-1)->currentMove != MOVE_NONE); assert((ss-1)->currentMove != MOVE_NULL); - MovePicker mp(pos, ttMove, H, pos.captured_piece_type()); + MovePicker mp(pos, ttMove, Hist, pos.captured_piece_type()); CheckInfo ci(pos); while ((move = mp.next_move()) != MOVE_NONE) @@ -773,11 +744,11 @@ namespace { } // Step 10. Internal iterative deepening - if ( depth >= IIDDepth[PvNode] + if ( depth >= (PvNode ? 5 * ONE_PLY : 8 * ONE_PLY) && ttMove == MOVE_NONE - && (PvNode || (!inCheck && ss->eval + IIDMargin >= beta))) + && (PvNode || (!inCheck && ss->staticEval + Value(256) >= beta))) { - Depth d = (PvNode ? depth - 2 * ONE_PLY : depth / 2); + Depth d = depth - 2 * ONE_PLY - (PvNode ? DEPTH_ZERO : depth / 4); ss->skipNullMove = true; search(pos, ss, alpha, beta, d); @@ -789,12 +760,12 @@ namespace { split_point_start: // At split points actual search starts from here - MovePicker mp(pos, ttMove, depth, H, ss, PvNode ? -VALUE_INFINITE : beta); + MovePicker mp(pos, ttMove, depth, Hist, ss, PvNode ? -VALUE_INFINITE : beta); CheckInfo ci(pos); - futilityBase = ss->eval + ss->evalMargin; + value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc singularExtensionNode = !RootNode && !SpNode - && depth >= SingularExtensionDepth[PvNode] + && depth >= (PvNode ? 6 * ONE_PLY : 8 * ONE_PLY) && ttMove != MOVE_NONE && !excludedMove // Recursive singular search is not allowed && (tte->type() & BOUND_LOWER) @@ -802,10 +773,7 @@ split_point_start: // At split points actual search starts from here // Step 11. Loop through moves // Loop through all pseudo-legal moves until no moves remain or a beta cutoff occurs - while ( bestValue < beta - && (move = mp.next_move()) != MOVE_NONE - && !thisThread->cutoff_occurred() - && !Signals.stop) + while ((move = mp.next_move()) != MOVE_NONE) { assert(is_ok(move)); @@ -818,14 +786,14 @@ split_point_start: // At split points actual search starts from here if (RootNode && !std::count(RootMoves.begin() + PVIdx, RootMoves.end(), move)) continue; - // At PV and SpNode nodes we want all moves to be legal since the beginning - if ((PvNode || SpNode) && !pos.pl_move_is_legal(move, ci.pinned)) - continue; - if (SpNode) { - moveCount = ++sp->moveCount; - sp->mutex.unlock(); + // Shared counter cannot be decremented later if move turns out to be illegal + if (!pos.pl_move_is_legal(move, ci.pinned)) + continue; + + moveCount = ++splitPoint->moveCount; + splitPoint->mutex.unlock(); } else moveCount++; @@ -834,17 +802,23 @@ split_point_start: // At split points actual search starts from here { Signals.firstRootMove = (moveCount == 1); - if (thisThread == Threads.main_thread() && Time::now() - SearchTime > 2000) + if (thisThread == Threads.main_thread() && Time::now() - SearchTime > 3000) sync_cout << "info depth " << depth / ONE_PLY - << " currmove " << move_to_uci(move, Chess960) + << " currmove " << move_to_uci(move, pos.is_chess960()) << " currmovenumber " << moveCount + PVIdx << sync_endl; } - isPvMove = (PvNode && moveCount <= 1); + ext = DEPTH_ZERO; captureOrPromotion = pos.is_capture_or_promotion(move); givesCheck = pos.move_gives_check(move, ci); - dangerous = givesCheck || is_dangerous(pos, move, captureOrPromotion); - ext = DEPTH_ZERO; + dangerous = givesCheck + || pos.is_passed_pawn_push(move) + || type_of(move) == CASTLE + || ( captureOrPromotion // Entering a pawn endgame? + && type_of(pos.piece_on(to_sq(move))) != PAWN + && type_of(move) == NORMAL + && ( pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) + - PieceValue[MG][pos.piece_on(to_sq(move))] == VALUE_ZERO)); // Step 12. Extend checks and, in PV nodes, also dangerous moves if (PvNode && dangerous) @@ -859,11 +833,13 @@ split_point_start: // At split points actual search starts from here // on all the other moves but the ttMove, if result is lower than ttValue minus // a margin then we extend ttMove. if ( singularExtensionNode - && !ext && move == ttMove + && !ext && pos.pl_move_is_legal(move, ci.pinned) && abs(ttValue) < VALUE_KNOWN_WIN) { + assert(ttValue != VALUE_NONE); + Value rBeta = ttValue - int(depth); ss->excludedMove = move; ss->skipNullMove = true; @@ -883,15 +859,16 @@ split_point_start: // At split points actual search starts from here && !captureOrPromotion && !inCheck && !dangerous - && move != ttMove - && (bestValue > VALUE_MATED_IN_MAX_PLY || bestValue == -VALUE_INFINITE)) + /* && move != ttMove Already implicit in the next condition */ + && bestValue > VALUE_MATED_IN_MAX_PLY) { // Move count based pruning - if ( moveCount >= futility_move_count(depth) - && (!threatMove || !connected_threat(pos, move, threatMove))) + if ( depth < 16 * ONE_PLY + && moveCount >= FutilityMoveCounts[depth] + && (!threatMove || !refutes(pos, move, threatMove))) { if (SpNode) - sp->mutex.lock(); + splitPoint->mutex.lock(); continue; } @@ -900,35 +877,47 @@ split_point_start: // At split points actual search starts from here // We illogically ignore reduction condition depth >= 3*ONE_PLY for predicted depth, // but fixing this made program slightly weaker. Depth predictedDepth = newDepth - reduction(depth, moveCount); - futilityValue = futilityBase + futility_margin(predictedDepth, moveCount) - + H.gain(pos.piece_moved(move), to_sq(move)); + futilityValue = ss->staticEval + ss->evalMargin + futility_margin(predictedDepth, moveCount) + + Gain[pos.piece_moved(move)][to_sq(move)]; if (futilityValue < beta) { - if (SpNode) - sp->mutex.lock(); + bestValue = std::max(bestValue, futilityValue); + if (SpNode) + { + splitPoint->mutex.lock(); + if (bestValue > splitPoint->bestValue) + splitPoint->bestValue = bestValue; + } continue; } // Prune moves with negative SEE at low depths - if ( predictedDepth < 2 * ONE_PLY + if ( predictedDepth < 4 * ONE_PLY && pos.see_sign(move) < 0) { if (SpNode) - sp->mutex.lock(); + splitPoint->mutex.lock(); continue; } + + // We have not pruned the move that will be searched, but remember how + // far in the move list we are to be more aggressive in the child node. + ss->futilityMoveCount = moveCount; } + else + ss->futilityMoveCount = 0; // Check for legality only before to do the move - if (!pos.pl_move_is_legal(move, ci.pinned)) + if (!RootNode && !SpNode && !pos.pl_move_is_legal(move, ci.pinned)) { moveCount--; continue; } + pvMove = PvNode && moveCount == 1; ss->currentMove = move; if (!SpNode && !captureOrPromotion && playedMoveCount < 64) movesSearched[playedMoveCount++] = move; @@ -939,15 +928,17 @@ split_point_start: // At split points actual search starts from here // Step 15. Reduced depth search (LMR). If the move fails high will be // re-searched at full depth. if ( depth > 3 * ONE_PLY - && !isPvMove + && !pvMove && !captureOrPromotion && !dangerous - && ss->killers[0] != move - && ss->killers[1] != move) + && move != ttMove + && move != ss->killers[0] + && move != ss->killers[1]) { ss->reduction = reduction(depth, moveCount); Depth d = std::max(newDepth - ss->reduction, ONE_PLY); - alpha = SpNode ? sp->alpha : alpha; + if (SpNode) + alpha = splitPoint->alpha; value = -search(pos, ss+1, -(alpha+1), -alpha, d); @@ -955,23 +946,28 @@ split_point_start: // At split points actual search starts from here ss->reduction = DEPTH_ZERO; } else - doFullDepthSearch = !isPvMove; + doFullDepthSearch = !pvMove; // Step 16. Full depth search, when LMR is skipped or fails high if (doFullDepthSearch) { - alpha = SpNode ? sp->alpha : alpha; - value = newDepth < ONE_PLY ? -qsearch(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO) + if (SpNode) + alpha = splitPoint->alpha; + + value = newDepth < ONE_PLY ? + givesCheck ? -qsearch(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO) + : -qsearch(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO) : - search(pos, ss+1, -(alpha+1), -alpha, newDepth); } // Only for PV nodes 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 to fail low with value <= alpha and to try another move. - if (PvNode && (isPvMove || (value > alpha && (RootNode || value < beta)))) - value = newDepth < ONE_PLY ? -qsearch(pos, ss+1, -beta, -alpha, DEPTH_ZERO) + if (PvNode && (pvMove || (value > alpha && (RootNode || value < beta)))) + value = newDepth < ONE_PLY ? + givesCheck ? -qsearch(pos, ss+1, -beta, -alpha, DEPTH_ZERO) + : -qsearch(pos, ss+1, -beta, -alpha, DEPTH_ZERO) : - search(pos, ss+1, -beta, -alpha, newDepth); - // Step 17. Undo move pos.undo_move(move); @@ -980,21 +976,24 @@ split_point_start: // At split points actual search starts from here // Step 18. Check for new best move if (SpNode) { - sp->mutex.lock(); - bestValue = sp->bestValue; - alpha = sp->alpha; + splitPoint->mutex.lock(); + bestValue = splitPoint->bestValue; + alpha = splitPoint->alpha; } // Finished searching the move. If Signals.stop is true, the search // was aborted because the user interrupted the search or because we // ran out of time. In this case, the return value of the search cannot // be trusted, and we don't update the best move and/or PV. - if (RootNode && !Signals.stop) + if (Signals.stop || thisThread->cutoff_occurred()) + return value; // To avoid returning VALUE_INFINITE + + if (RootNode) { RootMove& rm = *std::find(RootMoves.begin(), RootMoves.end(), move); // PV move or new best move ? - if (isPvMove || value > alpha) + if (pvMove || value > alpha) { rm.score = value; rm.extract_pv_from_tt(pos); @@ -1002,7 +1001,7 @@ split_point_start: // At split points actual search starts from here // 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 (!isPvMove && MultiPV == 1) + if (!pvMove) BestMoveChanges++; } else @@ -1010,91 +1009,96 @@ split_point_start: // At split points actual search starts from here // is not a problem when sorting becuase sort is stable and move // position in the list is preserved, just the PV is pushed up. rm.score = -VALUE_INFINITE; - } if (value > bestValue) { - bestValue = value; - bestMove = move; + bestValue = SpNode ? splitPoint->bestValue = value : value; - if ( PvNode - && value > alpha - && value < beta) // We want always alpha < beta - alpha = value; - - if (SpNode && !thisThread->cutoff_occurred()) + if (value > alpha) { - sp->bestValue = value; - sp->bestMove = move; - sp->alpha = alpha; + bestMove = SpNode ? splitPoint->bestMove = move : move; - if (value >= beta) - sp->cutoff = true; + if (PvNode && value < beta) // Update alpha! Always alpha < beta + alpha = SpNode ? splitPoint->alpha = value : value; + else + { + assert(value >= beta); // Fail high + + if (SpNode) + splitPoint->cutoff = true; + + break; + } } } - // Step 19. Check for split + // Step 19. Check for splitting the search if ( !SpNode - && depth >= Threads.min_split_depth() - && bestValue < beta - && Threads.available_slave_exists(thisThread) - && !Signals.stop - && !thisThread->cutoff_occurred()) - bestValue = Threads.split(pos, ss, alpha, beta, bestValue, &bestMove, - depth, threatMove, moveCount, &mp, NT); + && depth >= Threads.minimumSplitDepth + && Threads.available_slave(thisThread) + && thisThread->splitPointsSize < MAX_SPLITPOINTS_PER_THREAD) + { + assert(bestValue < beta); + + thisThread->split(pos, ss, alpha, beta, &bestValue, &bestMove, + depth, threatMove, moveCount, &mp, NT); + if (bestValue >= beta) + break; + } } + if (SpNode) + return bestValue; + // Step 20. Check for mate and stalemate // All legal moves have been searched and if there are no legal moves, it // must be mate or stalemate. Note that we can have a false positive in // case of Signals.stop or thread.cutoff_occurred() are set, but this is // harmless because return value is discarded anyhow in the parent nodes. // If we are in a singular extension search then return a fail low score. + // A split node has at least one move, the one tried before to be splitted. if (!moveCount) - return excludedMove ? oldAlpha : inCheck ? mated_in(ss->ply) : VALUE_DRAW; + return excludedMove ? alpha + : inCheck ? mated_in(ss->ply) : DrawValue[pos.side_to_move()]; // If we have pruned all the moves without searching return a fail-low score if (bestValue == -VALUE_INFINITE) { assert(!playedMoveCount); - bestValue = oldAlpha; + bestValue = alpha; } - // Step 21. Update tables - // Update transposition table entry, killers and history - if (!SpNode && !Signals.stop && !thisThread->cutoff_occurred()) + if (bestValue >= beta) // Failed high { - move = bestValue <= oldAlpha ? MOVE_NONE : bestMove; - bt = bestValue <= oldAlpha ? BOUND_UPPER - : bestValue >= beta ? BOUND_LOWER : BOUND_EXACT; + TT.store(posKey, value_to_tt(bestValue, ss->ply), BOUND_LOWER, depth, + bestMove, ss->staticEval, ss->evalMargin); - TT.store(posKey, value_to_tt(bestValue, ss->ply), bt, depth, move, ss->eval, ss->evalMargin); - - // Update killers and history for non capture cut-off moves - if ( bestValue >= beta - && !pos.is_capture_or_promotion(move) - && !inCheck) + if (!pos.is_capture_or_promotion(bestMove) && !inCheck) { - if (move != ss->killers[0]) + if (bestMove != ss->killers[0]) { ss->killers[1] = ss->killers[0]; - ss->killers[0] = move; + ss->killers[0] = bestMove; } // Increase history value of the cut-off move Value bonus = Value(int(depth) * int(depth)); - H.add(pos.piece_moved(move), to_sq(move), bonus); + Hist.update(pos.piece_moved(bestMove), to_sq(bestMove), bonus); // Decrease history of all the other played non-capture moves for (int i = 0; i < playedMoveCount - 1; i++) { Move m = movesSearched[i]; - H.add(pos.piece_moved(m), to_sq(m), -bonus); + Hist.update(pos.piece_moved(m), to_sq(m), -bonus); } } } + else // Failed low or PV search + TT.store(posKey, value_to_tt(bestValue, ss->ply), + PvNode && bestMove != MOVE_NONE ? BOUND_EXACT : BOUND_UPPER, + depth, bestMove, ss->staticEval, ss->evalMargin); assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); @@ -1106,74 +1110,85 @@ split_point_start: // At split points actual search starts from here // search function when the remaining depth is zero (or, to be more precise, // less than ONE_PLY). - template + template Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { const bool PvNode = (NT == PV); assert(NT == PV || NT == NonPV); + assert(InCheck == !!pos.checkers()); assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE); - assert((alpha == beta - 1) || PvNode); + assert(PvNode || (alpha == beta - 1)); assert(depth <= DEPTH_ZERO); StateInfo st; - Move ttMove, move, bestMove; - Value ttValue, bestValue, value, evalMargin, futilityValue, futilityBase; - bool inCheck, enoughMaterial, givesCheck, evasionPrunable; const TTEntry* tte; + Key posKey; + Move ttMove, move, bestMove; + Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha; + bool givesCheck, enoughMaterial, evasionPrunable; Depth ttDepth; - Bound bt; - Value oldAlpha = alpha; + + // To flag BOUND_EXACT a node with eval above alpha and no available moves + if (PvNode) + oldAlpha = alpha; ss->currentMove = bestMove = MOVE_NONE; ss->ply = (ss-1)->ply + 1; // Check for an instant draw or maximum ply reached - if (pos.is_draw() || ss->ply > MAX_PLY) - return VALUE_DRAW; + if (pos.is_draw() || ss->ply > MAX_PLY) + return DrawValue[pos.side_to_move()]; // 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 // only two types of depth in TT: DEPTH_QS_CHECKS or DEPTH_QS_NO_CHECKS. - inCheck = pos.in_check(); - ttDepth = (inCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS : DEPTH_QS_NO_CHECKS); + ttDepth = InCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS + : DEPTH_QS_NO_CHECKS; // Transposition table lookup. At PV nodes, we don't use the TT for // pruning, but only for move ordering. - tte = TT.probe(pos.key()); - ttMove = (tte ? tte->move() : MOVE_NONE); - ttValue = tte ? value_from_tt(tte->value(),ss->ply) : VALUE_ZERO; + posKey = pos.key(); + tte = TT.probe(posKey); + ttMove = tte ? tte->move() : MOVE_NONE; + ttValue = tte ? value_from_tt(tte->value(),ss->ply) : VALUE_NONE; - if (!PvNode && tte && can_return_tt(tte, ttDepth, ttValue, beta)) + if ( tte + && tte->depth() >= ttDepth + && ttValue != VALUE_NONE // Only in case of TT access race + && ( PvNode ? tte->type() == BOUND_EXACT + : ttValue >= beta ? (tte->type() & BOUND_LOWER) + : (tte->type() & BOUND_UPPER))) { ss->currentMove = ttMove; // Can be MOVE_NONE return ttValue; } // Evaluate the position statically - if (inCheck) + if (InCheck) { + ss->staticEval = ss->evalMargin = VALUE_NONE; bestValue = futilityBase = -VALUE_INFINITE; - ss->eval = evalMargin = VALUE_NONE; enoughMaterial = false; } else { if (tte) { - assert(tte->static_value() != VALUE_NONE); - - evalMargin = tte->static_value_margin(); - ss->eval = bestValue = tte->static_value(); + // Never assume anything on values stored in TT + if ( (ss->staticEval = bestValue = tte->eval_value()) == VALUE_NONE + ||(ss->evalMargin = tte->eval_margin()) == VALUE_NONE) + ss->staticEval = bestValue = evaluate(pos, ss->evalMargin); } else - ss->eval = bestValue = evaluate(pos, evalMargin); + ss->staticEval = bestValue = evaluate(pos, ss->evalMargin); // 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->eval, evalMargin); + TT.store(pos.key(), value_to_tt(bestValue, ss->ply), BOUND_LOWER, + DEPTH_NONE, MOVE_NONE, ss->staticEval, ss->evalMargin); return bestValue; } @@ -1181,7 +1196,7 @@ split_point_start: // At split points actual search starts from here if (PvNode && bestValue > alpha) alpha = bestValue; - futilityBase = ss->eval + evalMargin + FutilityMarginQS; + futilityBase = ss->staticEval + ss->evalMargin + Value(128); enoughMaterial = pos.non_pawn_material(pos.side_to_move()) > RookValueMg; } @@ -1189,12 +1204,11 @@ split_point_start: // At split points actual search starts from here // to search the moves. Because the depth is <= 0 here, only captures, // queen promotions and checks (only if depth >= DEPTH_QS_CHECKS) will // be generated. - MovePicker mp(pos, ttMove, depth, H, to_sq((ss-1)->currentMove)); + MovePicker mp(pos, ttMove, depth, Hist, to_sq((ss-1)->currentMove)); CheckInfo ci(pos); // Loop through the moves until no moves remain or a beta cutoff occurs - while ( bestValue < beta - && (move = mp.next_move()) != MOVE_NONE) + while ((move = mp.next_move()) != MOVE_NONE) { assert(is_ok(move)); @@ -1202,7 +1216,7 @@ split_point_start: // At split points actual search starts from here // Futility pruning if ( !PvNode - && !inCheck + && !InCheck && !givesCheck && move != ttMove && enoughMaterial @@ -1210,34 +1224,36 @@ split_point_start: // At split points actual search starts from here && !pos.is_passed_pawn_push(move)) { futilityValue = futilityBase - + PieceValue[Eg][pos.piece_on(to_sq(move))] + + PieceValue[EG][pos.piece_on(to_sq(move))] + (type_of(move) == ENPASSANT ? PawnValueEg : VALUE_ZERO); if (futilityValue < beta) { - if (futilityValue > bestValue) - bestValue = futilityValue; - + bestValue = std::max(bestValue, futilityValue); continue; } - // Prune moves with negative or equal SEE + // Prune moves with negative or equal SEE and also moves with positive + // SEE where capturing piece loses a tempo and SEE < beta - futilityBase. if ( futilityBase < beta && depth < DEPTH_ZERO - && pos.see(move) <= 0) + && pos.see(move, beta - futilityBase) <= 0) + { + bestValue = std::max(bestValue, futilityBase); continue; + } } // Detect non-capture evasions that are candidate to be pruned evasionPrunable = !PvNode - && inCheck + && InCheck && bestValue > VALUE_MATED_IN_MAX_PLY && !pos.is_capture(move) && !pos.can_castle(pos.side_to_move()); // Don't search moves with negative SEE values if ( !PvNode - && (!inCheck || evasionPrunable) + && (!InCheck || evasionPrunable) && move != ttMove && type_of(move) != PROMOTION && pos.see_sign(move) < 0) @@ -1245,11 +1261,11 @@ split_point_start: // At split points actual search starts from here // Don't search useless checks if ( !PvNode - && !inCheck + && !InCheck && givesCheck && move != ttMove && !pos.is_capture_or_promotion(move) - && ss->eval + PawnValueMg / 4 < beta + && ss->staticEval + PawnValueMg / 4 < beta && !check_is_dangerous(pos, move, futilityBase, beta)) continue; @@ -1261,35 +1277,43 @@ split_point_start: // At split points actual search starts from here // Make and search the move pos.do_move(move, st, ci, givesCheck); - value = -qsearch(pos, ss+1, -beta, -alpha, depth-ONE_PLY); + value = givesCheck ? -qsearch(pos, ss+1, -beta, -alpha, depth - ONE_PLY) + : -qsearch(pos, ss+1, -beta, -alpha, depth - ONE_PLY); pos.undo_move(move); assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); - // New best move? + // Check for new best move if (value > bestValue) { bestValue = value; - bestMove = move; - if ( PvNode - && value > alpha - && value < beta) // We want always alpha < beta - alpha = value; + if (value > alpha) + { + if (PvNode && value < beta) // Update alpha here! Always alpha < beta + { + alpha = value; + bestMove = move; + } + else // Fail high + { + TT.store(posKey, value_to_tt(value, ss->ply), BOUND_LOWER, + ttDepth, move, ss->staticEval, ss->evalMargin); + + return value; + } + } } } // All legal moves have been searched. A special case: If we're in check // and no legal moves were found, it is checkmate. - if (inCheck && bestValue == -VALUE_INFINITE) + if (InCheck && bestValue == -VALUE_INFINITE) return mated_in(ss->ply); // Plies to mate from the root - // Update transposition table - move = bestValue <= oldAlpha ? MOVE_NONE : bestMove; - bt = bestValue <= oldAlpha ? BOUND_UPPER - : bestValue >= beta ? BOUND_LOWER : BOUND_EXACT; - - TT.store(pos.key(), value_to_tt(bestValue, ss->ply), bt, ttDepth, move, ss->eval, evalMargin); + TT.store(posKey, value_to_tt(bestValue, ss->ply), + PvNode && bestValue > oldAlpha ? BOUND_EXACT : BOUND_UPPER, + ttDepth, bestMove, ss->staticEval, ss->evalMargin); assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); @@ -1297,112 +1321,16 @@ split_point_start: // At split points actual search starts from here } - // check_is_dangerous() tests if a checking move can be pruned in qsearch(). - // bestValue is updated only when returning false because in that case move - // will be pruned. - - bool check_is_dangerous(Position &pos, Move move, Value futilityBase, Value beta) - { - Bitboard b, occ, oldAtt, newAtt, kingAtt; - Square from, to, ksq; - Piece pc; - Color them; - - from = from_sq(move); - to = to_sq(move); - them = ~pos.side_to_move(); - ksq = pos.king_square(them); - kingAtt = pos.attacks_from(ksq); - pc = pos.piece_moved(move); - - occ = pos.pieces() ^ from ^ ksq; - oldAtt = pos.attacks_from(pc, from, occ); - newAtt = pos.attacks_from(pc, to, occ); - - // Rule 1. Checks which give opponent's king at most one escape square are dangerous - b = kingAtt & ~pos.pieces(them) & ~newAtt & ~(1ULL << to); - - if (!more_than_one(b)) - return true; - - // Rule 2. Queen contact check is very dangerous - if (type_of(pc) == QUEEN && (kingAtt & to)) - return true; - - // Rule 3. Creating new double threats with checks - b = pos.pieces(them) & newAtt & ~oldAtt & ~(1ULL << ksq); - while (b) - { - // Note that here we generate illegal "double move"! - if (futilityBase + PieceValue[Eg][pos.piece_on(pop_lsb(&b))] >= beta) - return true; - } - - return false; - } - - - // connected_moves() tests whether two moves are 'connected' in the sense - // that the first move somehow made the second move possible (for instance - // if the moving piece is the same in both moves). The first move is assumed - // to be the move that was made to reach the current position, while the - // second move is assumed to be a move from the current position. - - bool connected_moves(const Position& pos, Move m1, Move m2) { - - Square f1, t1, f2, t2; - Piece p1, p2; - Square ksq; - - assert(is_ok(m1)); - assert(is_ok(m2)); - - // Case 1: The moving piece is the same in both moves - f2 = from_sq(m2); - t1 = to_sq(m1); - if (f2 == t1) - return true; - - // Case 2: The destination square for m2 was vacated by m1 - t2 = to_sq(m2); - f1 = from_sq(m1); - if (t2 == f1) - return true; - - // Case 3: Moving through the vacated square - p2 = pos.piece_on(f2); - if (piece_is_slider(p2) && (between_bb(f2, t2) & f1)) - return true; - - // Case 4: The destination square for m2 is defended by the moving piece in m1 - p1 = pos.piece_on(t1); - if (pos.attacks_from(p1, t1) & t2) - return true; - - // Case 5: Discovered check, checking piece is the piece moved in m1 - ksq = pos.king_square(pos.side_to_move()); - if ( piece_is_slider(p1) - && (between_bb(t1, ksq) & f2) - && (pos.attacks_from(p1, t1, pos.pieces() ^ f2) & ksq)) - return true; - - return false; - } - - // value_to_tt() adjusts a mate score from "plies to mate from the root" to // "plies to mate from the current position". Non-mate scores are unchanged. // The function is called before storing a value to the transposition table. Value value_to_tt(Value v, int ply) { - if (v >= VALUE_MATE_IN_MAX_PLY) - return v + ply; + assert(v != VALUE_NONE); - if (v <= VALUE_MATED_IN_MAX_PLY) - return v - ply; - - return v; + return v >= VALUE_MATE_IN_MAX_PLY ? v + ply + : v <= VALUE_MATED_IN_MAX_PLY ? v - ply : v; } @@ -1412,91 +1340,142 @@ split_point_start: // At split points actual search starts from here Value value_from_tt(Value v, int ply) { - if (v >= VALUE_MATE_IN_MAX_PLY) - return v - ply; - - if (v <= VALUE_MATED_IN_MAX_PLY) - return v + ply; - - return v; + return v == VALUE_NONE ? VALUE_NONE + : v >= VALUE_MATE_IN_MAX_PLY ? v - ply + : v <= VALUE_MATED_IN_MAX_PLY ? v + ply : v; } - // connected_threat() tests whether it is safe to forward prune a move or if - // is somehow connected to the threat move returned by null search. + // check_is_dangerous() tests if a checking move can be pruned in qsearch() - bool connected_threat(const Position& pos, Move m, Move threat) { + bool check_is_dangerous(const Position& pos, Move move, Value futilityBase, Value beta) + { + Piece pc = pos.piece_moved(move); + Square from = from_sq(move); + Square to = to_sq(move); + Color them = ~pos.side_to_move(); + Square ksq = pos.king_square(them); + Bitboard enemies = pos.pieces(them); + Bitboard kingAtt = pos.attacks_from(ksq); + Bitboard occ = pos.pieces() ^ from ^ ksq; + Bitboard oldAtt = pos.attacks_from(pc, from, occ); + Bitboard newAtt = pos.attacks_from(pc, to, occ); - assert(is_ok(m)); - assert(is_ok(threat)); - assert(!pos.is_capture_or_promotion(m)); - assert(!pos.is_passed_pawn_push(m)); - - Square mfrom, mto, tfrom, tto; - - mfrom = from_sq(m); - mto = to_sq(m); - tfrom = from_sq(threat); - tto = to_sq(threat); - - // Case 1: Don't prune moves which move the threatened piece - if (mfrom == tto) + // Checks which give opponent's king at most one escape square are dangerous + if (!more_than_one(kingAtt & ~(enemies | newAtt | to))) return true; - // Case 2: If the threatened piece has value less than or equal to the - // value of the threatening piece, don't prune moves which defend it. - if ( pos.is_capture(threat) - && ( PieceValue[Mg][pos.piece_on(tfrom)] >= PieceValue[Mg][pos.piece_on(tto)] - || type_of(pos.piece_on(tfrom)) == KING) - && pos.move_attacks_square(m, tto)) + // Queen contact check is very dangerous + if (type_of(pc) == QUEEN && (kingAtt & to)) return true; - // Case 3: If the moving piece in the threatened move is a slider, don't - // prune safe moves which block its ray. - if ( piece_is_slider(pos.piece_on(tfrom)) - && (between_bb(tfrom, tto) & mto) - && pos.see_sign(m) >= 0) + // Creating new double threats with checks is dangerous + Bitboard b = (enemies ^ ksq) & newAtt & ~oldAtt; + while (b) + { + // Note that here we generate illegal "double move"! + if (futilityBase + PieceValue[EG][pos.piece_on(pop_lsb(&b))] >= beta) + return true; + } + + return false; + } + + + // allows() tests whether the 'first' move at previous ply somehow makes the + // 'second' move possible, for instance if the moving piece is the same in + // both moves. Normally the second move is the threat (the best move returned + // from a null search that fails low). + + bool allows(const Position& pos, Move first, Move second) { + + assert(is_ok(first)); + assert(is_ok(second)); + assert(color_of(pos.piece_on(from_sq(second))) == ~pos.side_to_move()); + assert(color_of(pos.piece_on(to_sq(first))) == ~pos.side_to_move()); + + Square m1from = from_sq(first); + Square m2from = from_sq(second); + Square m1to = to_sq(first); + Square m2to = to_sq(second); + + // The piece is the same or second's destination was vacated by the first move + if (m1to == m2from || m2to == m1from) + return true; + + // Second one moves through the square vacated by first one + if (between_bb(m2from, m2to) & m1from) + return true; + + // Second's destination is defended by the first move's piece + Bitboard m1att = pos.attacks_from(pos.piece_on(m1to), m1to, pos.pieces() ^ m2from); + if (m1att & m2to) + return true; + + // Second move gives a discovered check through the first's checking piece + if (m1att & pos.king_square(pos.side_to_move())) + { + assert(between_bb(m1to, pos.king_square(pos.side_to_move())) & m2from); + return true; + } + + return false; + } + + + // refutes() tests whether a 'first' move is able to defend against a 'second' + // opponent's move. In this case will not be pruned. Normally the second move + // is the threat (the best move returned from a null search that fails low). + + bool refutes(const Position& pos, Move first, Move second) { + + assert(is_ok(first)); + assert(is_ok(second)); + + Square m1from = from_sq(first); + Square m2from = from_sq(second); + Square m1to = to_sq(first); + Square m2to = to_sq(second); + + // Don't prune moves of the threatened piece + if (m1from == m2to) + return true; + + // If the threatened piece has value less than or equal to the value of the + // threat piece, don't prune moves which defend it. + if ( pos.is_capture(second) + && ( PieceValue[MG][pos.piece_on(m2from)] >= PieceValue[MG][pos.piece_on(m2to)] + || type_of(pos.piece_on(m2from)) == KING)) + { + // Update occupancy as if the piece and the threat are moving + Bitboard occ = pos.pieces() ^ m1from ^ m1to ^ m2from; + Piece piece = pos.piece_on(m1from); + + // The moved piece attacks the square 'tto' ? + if (pos.attacks_from(piece, m1to, occ) & m2to) + return true; + + // Scan for possible X-ray attackers behind the moved piece + Bitboard xray = (attacks_bb< ROOK>(m2to, occ) & pos.pieces(color_of(piece), QUEEN, ROOK)) + | (attacks_bb(m2to, occ) & pos.pieces(color_of(piece), QUEEN, BISHOP)); + + // Verify attackers are triggered by our move and not already existing + if (xray && (xray ^ (xray & pos.attacks_from(m2to)))) + return true; + } + + // Don't prune safe moves which block the threat path + if ((between_bb(m2from, m2to) & m1to) && pos.see_sign(first) >= 0) return true; return false; } - // can_return_tt() returns true if a transposition table score can be used to - // cut-off at a given point in search. - - bool can_return_tt(const TTEntry* tte, Depth depth, Value v, Value beta) { - - return ( tte->depth() >= depth - || v >= std::max(VALUE_MATE_IN_MAX_PLY, beta) - || v < std::min(VALUE_MATED_IN_MAX_PLY, beta)) - - && ( ((tte->type() & BOUND_LOWER) && v >= beta) - || ((tte->type() & BOUND_UPPER) && v < beta)); - } - - - // refine_eval() returns the transposition table score if possible, otherwise - // falls back on static position evaluation. - - Value refine_eval(const TTEntry* tte, Value v, Value defaultEval) { - - assert(tte); - - if ( ((tte->type() & BOUND_LOWER) && v >= defaultEval) - || ((tte->type() & BOUND_UPPER) && v < defaultEval)) - return v; - - return defaultEval; - } - - // When playing with strength handicap choose best move among the MultiPV set - // using a statistical rule dependent on SkillLevel. Idea by Heinz van Saanen. + // using a statistical rule dependent on 'level'. Idea by Heinz van Saanen. - Move do_skill_level() { - - assert(MultiPV > 1); + Move Skill::pick_move() { static RKISS rk; @@ -1505,21 +1484,20 @@ split_point_start: // At split points actual search starts from here rk.rand(); // RootMoves are already sorted by score in descending order - size_t size = std::min(MultiPV, RootMoves.size()); - int variance = std::min(RootMoves[0].score - RootMoves[size - 1].score, PawnValueMg); - int weakness = 120 - 2 * SkillLevel; + int variance = std::min(RootMoves[0].score - RootMoves[PVSize - 1].score, PawnValueMg); + int weakness = 120 - 2 * level; int max_s = -VALUE_INFINITE; - Move best = MOVE_NONE; + best = MOVE_NONE; // Choose best move. For each move score we add two terms both dependent on // weakness, one deterministic and bigger for weaker moves, and one random, // then we choose the move with the resulting highest score. - for (size_t i = 0; i < size; i++) + for (size_t i = 0; i < PVSize; i++) { int s = RootMoves[i].score; // Don't allow crazy blunders even at very low skills - if (i > 0 && RootMoves[i-1].score > s + EasyMoveMargin) + if (i > 0 && RootMoves[i-1].score > s + 2 * PawnValueMg) break; // This is our magic formula @@ -1544,23 +1522,24 @@ split_point_start: // At split points actual search starts from here std::stringstream s; Time::point elaspsed = Time::now() - SearchTime + 1; + size_t uciPVSize = std::min((size_t)Options["MultiPV"], RootMoves.size()); int selDepth = 0; for (size_t i = 0; i < Threads.size(); i++) - if (Threads[i].maxPly > selDepth) - selDepth = Threads[i].maxPly; + if (Threads[i]->maxPly > selDepth) + selDepth = Threads[i]->maxPly; - for (size_t i = 0; i < std::min(UCIMultiPV, RootMoves.size()); i++) + for (size_t i = 0; i < uciPVSize; i++) { bool updated = (i <= PVIdx); if (depth == 1 && !updated) continue; - int d = (updated ? depth : depth - 1); - Value v = (updated ? RootMoves[i].score : RootMoves[i].prevScore); + int d = updated ? depth : depth - 1; + Value v = updated ? RootMoves[i].score : RootMoves[i].prevScore; - if (s.rdbuf()->in_avail()) + if (s.rdbuf()->in_avail()) // Not at first line s << "\n"; s << "info depth " << d @@ -1573,7 +1552,7 @@ split_point_start: // At split points actual search starts from here << " pv"; for (size_t j = 0; RootMoves[i].pv[j] != MOVE_NONE; j++) - s << " " << move_to_uci(RootMoves[i].pv[j], Chess960); + s << " " << move_to_uci(RootMoves[i].pv[j], pos.is_chess960()); } return s.str(); @@ -1591,29 +1570,28 @@ void RootMove::extract_pv_from_tt(Position& pos) { StateInfo state[MAX_PLY_PLUS_2], *st = state; TTEntry* tte; - int ply = 1; + int ply = 0; Move m = pv[0]; - assert(m != MOVE_NONE && pos.is_pseudo_legal(m)); - pv.clear(); - pv.push_back(m); - pos.do_move(m, *st++); - while ( (tte = TT.probe(pos.key())) != NULL - && (m = tte->move()) != MOVE_NONE // Local copy, TT entry could change - && pos.is_pseudo_legal(m) - && pos.pl_move_is_legal(m, pos.pinned_pieces()) - && ply < MAX_PLY - && (!pos.is_draw() || ply < 2)) - { + do { pv.push_back(m); - pos.do_move(m, *st++); - ply++; - } - pv.push_back(MOVE_NONE); - do pos.undo_move(pv[--ply]); while (ply); + assert(MoveList(pos).contains(pv[ply])); + + pos.do_move(pv[ply++], *st++); + tte = TT.probe(pos.key()); + + } while ( tte + && pos.is_pseudo_legal(m = tte->move()) // Local copy, TT could change + && pos.pl_move_is_legal(m, pos.pinned_pieces()) + && ply < MAX_PLY + && (!pos.is_draw() || ply < 2)); + + pv.push_back(MOVE_NONE); // Must be zero-terminating + + while (ply) pos.undo_move(pv[--ply]); } @@ -1625,27 +1603,21 @@ void RootMove::insert_pv_in_tt(Position& pos) { StateInfo state[MAX_PLY_PLUS_2], *st = state; TTEntry* tte; - Key k; - Value v, m = VALUE_NONE; int ply = 0; - assert(pv[ply] != MOVE_NONE && pos.is_pseudo_legal(pv[ply])); - do { - k = pos.key(); - tte = TT.probe(k); + tte = TT.probe(pos.key()); - // Don't overwrite existing correct entries - if (!tte || tte->move() != pv[ply]) - { - v = (pos.in_check() ? VALUE_NONE : evaluate(pos, m)); - TT.store(k, VALUE_NONE, BOUND_NONE, DEPTH_NONE, pv[ply], v, m); - } - pos.do_move(pv[ply], *st++); + if (!tte || tte->move() != pv[ply]) // Don't overwrite correct entries + TT.store(pos.key(), VALUE_NONE, BOUND_NONE, DEPTH_NONE, pv[ply], VALUE_NONE, VALUE_NONE); - } while (pv[++ply] != MOVE_NONE); + assert(MoveList(pos).contains(pv[ply])); - do pos.undo_move(pv[--ply]); while (ply); + pos.do_move(pv[ply++], *st++); + + } while (pv[ply] != MOVE_NONE); + + while (ply) pos.undo_move(pv[--ply]); } @@ -1653,33 +1625,29 @@ void RootMove::insert_pv_in_tt(Position& pos) { void Thread::idle_loop() { - // Pointer 'sp_master', if non-NULL, points to the active SplitPoint - // object for which the thread is the master. - const SplitPoint* sp_master = splitPointsCnt ? curSplitPoint : NULL; + // Pointer 'this_sp' is not null only if we are called from split(), and not + // at the thread creation. So it means we are the split point's master. + SplitPoint* this_sp = splitPointsSize ? activeSplitPoint : NULL; - assert(!sp_master || (sp_master->master == this && is_searching)); + assert(!this_sp || (this_sp->masterThread == this && searching)); - // If this thread is the master of a split point and all slaves have - // finished their work at this split point, return from the idle loop. - while (!sp_master || sp_master->slavesMask) + while (true) { - // If we are not searching, wait for a condition to be signaled - // instead of wasting CPU time polling for work. - while ( do_sleep - || do_exit - || (!is_searching && Threads.use_sleeping_threads())) + // If we are not searching, wait for a condition to be signaled instead of + // wasting CPU time polling for work. + while ((!searching && Threads.sleepWhileIdle) || exit) { - if (do_exit) + if (exit) { - assert(!sp_master); + assert(!this_sp); return; } - // Grab the lock to avoid races with Thread::wake_up() + // Grab the lock to avoid races with Thread::notify_one() mutex.lock(); - // If we are master and all slaves have finished don't go to sleep - if (sp_master && !sp_master->slavesMask) + // If we are master and all slaves have finished then exit idle_loop + if (this_sp && !this_sp->slavesMask) { mutex.unlock(); break; @@ -1687,23 +1655,23 @@ void Thread::idle_loop() { // Do sleep after retesting sleep conditions under lock protection, in // particular we need to avoid a deadlock in case a master thread has, - // in the meanwhile, allocated us and sent the wake_up() call before we - // had the chance to grab the lock. - if (do_sleep || !is_searching) + // in the meanwhile, allocated us and sent the notify_one() call before + // we had the chance to grab the lock. + if (!searching && !exit) sleepCondition.wait(mutex); mutex.unlock(); } // If this thread has been assigned work, launch a search - if (is_searching) + if (searching) { - assert(!do_sleep && !do_exit); + assert(!exit); Threads.mutex.lock(); - assert(is_searching); - SplitPoint* sp = curSplitPoint; + assert(searching); + SplitPoint* sp = activeSplitPoint; Threads.mutex.unlock(); @@ -1711,33 +1679,43 @@ void Thread::idle_loop() { Position pos(*sp->pos, this); memcpy(ss, sp->ss - 1, 4 * sizeof(Stack)); - (ss+1)->sp = sp; + (ss+1)->splitPoint = sp; sp->mutex.lock(); - if (sp->nodeType == Root) + assert(activePosition == NULL); + + activePosition = &pos; + + switch (sp->nodeType) { + case Root: search(pos, ss+1, sp->alpha, sp->beta, sp->depth); - else if (sp->nodeType == PV) + break; + case PV: search(pos, ss+1, sp->alpha, sp->beta, sp->depth); - else if (sp->nodeType == NonPV) + break; + case NonPV: search(pos, ss+1, sp->alpha, sp->beta, sp->depth); - else + break; + default: assert(false); + } - assert(is_searching); + assert(searching); - is_searching = false; + searching = false; + activePosition = NULL; sp->slavesMask &= ~(1ULL << idx); sp->nodes += pos.nodes_searched(); - // Wake up master thread so to allow it to return from the idle loop in - // case we are the last slave of the split point. - if ( Threads.use_sleeping_threads() - && this != sp->master + // Wake up master thread so to allow it to return from the idle loop + // in case we are the last slave of the split point. + if ( Threads.sleepWhileIdle + && this != sp->masterThread && !sp->slavesMask) { - assert(!sp->master->is_searching); - sp->master->wake_up(); + assert(!sp->masterThread->searching); + sp->masterThread->notify_one(); } // After releasing the lock we cannot access anymore any SplitPoint @@ -1746,6 +1724,17 @@ void Thread::idle_loop() { // unsafe because if we are exiting there is a chance are already freed. sp->mutex.unlock(); } + + // If this thread is the master of a split point and all slaves have finished + // their work at this split point, return from the idle loop. + if (this_sp && !this_sp->slavesMask) + { + this_sp->mutex.lock(); + bool finished = !this_sp->slavesMask; // Retest under lock protection + this_sp->mutex.unlock(); + if (finished) + return; + } } } @@ -1757,6 +1746,7 @@ void Thread::idle_loop() { void check_time() { static Time::point lastInfoTime = Time::now(); + int64_t nodes = 0; // Workaround silly 'uninitialized' gcc warning if (Time::now() - lastInfoTime >= 1000) { @@ -1767,6 +1757,36 @@ void check_time() { if (Limits.ponder) return; + if (Limits.nodes) + { + Threads.mutex.lock(); + + nodes = RootPos.nodes_searched(); + + // Loop across all split points and sum accumulated SplitPoint nodes plus + // all the currently active positions nodes. + for (size_t i = 0; i < Threads.size(); i++) + for (int j = 0; j < Threads[i]->splitPointsSize; j++) + { + SplitPoint& sp = Threads[i]->splitPoints[j]; + + sp.mutex.lock(); + + nodes += sp.nodes; + Bitboard sm = sp.slavesMask; + while (sm) + { + Position* pos = Threads[pop_lsb(&sm)]->activePosition; + if (pos) + nodes += pos->nodes_searched(); + } + + sp.mutex.unlock(); + } + + Threads.mutex.unlock(); + } + Time::point elapsed = Time::now() - SearchTime; bool stillAtFirstMove = Signals.firstRootMove && !Signals.failedLowAtRoot @@ -1776,6 +1796,7 @@ void check_time() { || stillAtFirstMove; if ( (Limits.use_time_management() && noMoreTime) - || (Limits.movetime && elapsed >= Limits.movetime)) + || (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 5f9b74d..b4fb26a 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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 @@ -38,15 +38,16 @@ namespace Search { /// has its own array of Stack objects, indexed by the current ply. struct Stack { - SplitPoint* sp; + SplitPoint* splitPoint; int ply; Move currentMove; Move excludedMove; Move killers[2]; Depth reduction; - Value eval; + Value staticEval; Value evalMargin; int skipNullMove; + int futilityMoveCount; }; @@ -56,12 +57,11 @@ struct Stack { /// all non-pv moves. struct RootMove { - RootMove(){} // Needed by sort() RootMove(Move m) : score(-VALUE_INFINITE), prevScore(-VALUE_INFINITE) { pv.push_back(m); pv.push_back(MOVE_NONE); } - bool operator<(const RootMove& m) const { return score < m.score; } + 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); @@ -80,9 +80,9 @@ struct RootMove { struct LimitsType { LimitsType() { memset(this, 0, sizeof(LimitsType)); } - bool use_time_management() const { return !(movetime | depth | nodes | infinite); } + bool use_time_management() const { return !(mate | movetime | depth | nodes | infinite); } - int time[2], inc[2], movestogo, depth, nodes, movetime, infinite, ponder; + int time[COLOR_NB], inc[COLOR_NB], movestogo, depth, nodes, movetime, mate, infinite, ponder; }; @@ -98,7 +98,8 @@ typedef std::auto_ptr > StateStackPtr; extern volatile SignalsType Signals; extern LimitsType Limits; extern std::vector RootMoves; -extern Position RootPosition; +extern Position RootPos; +extern Color RootColor; extern Time::point SearchTime; extern StateStackPtr SetupStates; diff --git a/DroidFish/jni/stockfish/thread.cpp b/DroidFish/jni/stockfish/thread.cpp index aac311f..f5b8b5e 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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,6 +17,7 @@ along with this program. If not, see . */ +#include // For std::count #include #include @@ -32,26 +33,24 @@ ThreadPool Threads; // Global object namespace { extern "C" { // start_routine() is the C function which is called when a new thread - // is launched. It is a wrapper to member function pointed by start_fn. + // is launched. It is a wrapper to the virtual function idle_loop(). - long start_routine(Thread* th) { (th->*(th->start_fn))(); return 0; } + long start_routine(Thread* th) { th->idle_loop(); return 0; } } } // Thread c'tor starts a newly-created thread of execution that will call -// the idle loop function pointed by start_fn going immediately to sleep. +// the the virtual function idle_loop(), going immediately to sleep. -Thread::Thread(Fn fn) { +Thread::Thread() /* : splitPoints() */ { // Value-initialization bug in MSVC - is_searching = do_exit = false; - maxPly = splitPointsCnt = 0; - curSplitPoint = NULL; - start_fn = fn; + searching = exit = false; + maxPly = splitPointsSize = 0; + activeSplitPoint = NULL; + activePosition = NULL; idx = Threads.size(); - do_sleep = (fn != &Thread::main_loop); // Avoid a race with start_searching() - if (!thread_create(handle, start_routine, this)) { std::cerr << "Failed to create thread number " << idx << std::endl; @@ -60,47 +59,49 @@ Thread::Thread(Fn fn) { } -// Thread d'tor waits for thread termination before to return. +// Thread d'tor waits for thread termination before to return Thread::~Thread() { - assert(do_sleep); - - do_exit = true; // Search must be already finished - wake_up(); + exit = true; // Search must be already finished + notify_one(); thread_join(handle); // Wait for thread termination } -// Thread::timer_loop() is where the timer thread waits maxPly milliseconds and -// then calls check_time(). If maxPly is 0 thread sleeps until is woken up. +// TimerThread::idle_loop() is where the timer thread waits msec milliseconds +// and then calls check_time(). If msec is 0 thread sleeps until is woken up. extern void check_time(); -void Thread::timer_loop() { +void TimerThread::idle_loop() { - while (!do_exit) + while (!exit) { mutex.lock(); - sleepCondition.wait_for(mutex, maxPly ? maxPly : INT_MAX); + + if (!exit) + sleepCondition.wait_for(mutex, msec ? msec : INT_MAX); + mutex.unlock(); - check_time(); + + if (msec) + check_time(); } } -// Thread::main_loop() is where the main thread is parked waiting to be started +// MainThread::idle_loop() is where the main thread is parked waiting to be started // when there is a new search. Main thread will launch all the slave threads. -void Thread::main_loop() { +void MainThread::idle_loop() { while (true) { mutex.lock(); - do_sleep = true; // Always return to sleep after a search - is_searching = false; + thinking = false; - while (do_sleep && !do_exit) + while (!thinking && !exit) { Threads.sleepCondition.notify_one(); // Wake up UI thread if needed sleepCondition.wait(mutex); @@ -108,22 +109,23 @@ void Thread::main_loop() { mutex.unlock(); - if (do_exit) + if (exit) return; - is_searching = true; + searching = true; Search::think(); - assert(is_searching); + assert(searching); + + searching = false; } } -// Thread::wake_up() wakes up the thread, normally at the beginning of the search -// or, if "sleeping threads" is used at split time. +// Thread::notify_one() wakes up the thread when there is some search to do -void Thread::wake_up() { +void Thread::notify_one() { mutex.lock(); sleepCondition.notify_one(); @@ -131,19 +133,12 @@ void Thread::wake_up() { } -// Thread::wait_for_stop_or_ponderhit() is called when the maximum depth is -// reached while the program is pondering. The point is to work around a wrinkle -// in the UCI protocol: When pondering, the engine is not allowed to give a -// "bestmove" before the GUI sends it a "stop" or "ponderhit" command. We simply -// wait here until one of these commands (that raise StopRequest) is sent and -// then return, after which the bestmove and pondermove will be printed. +// Thread::wait_for() set the thread to sleep until condition 'b' turns true -void Thread::wait_for_stop_or_ponderhit() { - - Signals.stopOnPonderhit = true; +void Thread::wait_for(volatile const bool& b) { mutex.lock(); - while (!Signals.stop) sleepCondition.wait(mutex);; + while (!b) sleepCondition.wait(mutex); mutex.unlock(); } @@ -153,7 +148,7 @@ void Thread::wait_for_stop_or_ponderhit() { bool Thread::cutoff_occurred() const { - for (SplitPoint* sp = curSplitPoint; sp; sp = sp->parent) + for (SplitPoint* sp = activeSplitPoint; sp; sp = sp->parentSplitPoint) if (sp->cutoff) return true; @@ -164,46 +159,47 @@ bool Thread::cutoff_occurred() const { // Thread::is_available_to() checks whether the thread is available to help the // thread 'master' at a split point. An obvious requirement is that thread must // be idle. With more than two threads, this is not sufficient: If the thread is -// the master of some active split point, it is only available as a slave to the -// slaves which are busy searching the split point at the top of slaves split -// point stack (the "helpful master concept" in YBWC terminology). +// the master of some split point, it is only available as a slave to the slaves +// which are busy searching the split point at the top of slaves split point +// stack (the "helpful master concept" in YBWC terminology). bool Thread::is_available_to(Thread* master) const { - if (is_searching) + if (searching) return false; // Make a local copy to be sure doesn't become zero under our feet while // testing next condition and so leading to an out of bound access. - int spCnt = splitPointsCnt; + int size = splitPointsSize; - // No active split points means that the thread is available as a slave for any + // No split points means that the thread is available as a slave for any // other thread otherwise apply the "helpful master" concept if possible. - return !spCnt || (splitPoints[spCnt - 1].slavesMask & (1ULL << master->idx)); + return !size || (splitPoints[size - 1].slavesMask & (1ULL << master->idx)); } -// init() is called at startup. Initializes lock and condition variable and -// launches requested threads sending them immediately to sleep. We cannot use +// init() is called at startup to create and launch requested threads, that will +// go immediately to sleep due to 'sleepWhileIdle' set to true. We cannot use // a c'tor becuase Threads is a static object and we need a fully initialized -// engine at this point due to allocation of endgames in Thread c'tor. +// engine at this point due to allocation of Endgames in Thread c'tor. void ThreadPool::init() { - timer = new Thread(&Thread::timer_loop); - threads.push_back(new Thread(&Thread::main_loop)); + sleepWhileIdle = true; + timer = new TimerThread(); + push_back(new MainThread()); read_uci_options(); } -// exit() cleanly terminates the threads before the program exits. +// exit() cleanly terminates the threads before the program exits void ThreadPool::exit() { - for (size_t i = 0; i < threads.size(); i++) - delete threads[i]; + delete timer; // As first because check_time() accesses threads data - delete timer; + for (iterator it = begin(); it != end(); ++it) + delete *it; } @@ -216,221 +212,170 @@ void ThreadPool::read_uci_options() { maxThreadsPerSplitPoint = Options["Max Threads per Split Point"]; minimumSplitDepth = Options["Min Split Depth"] * ONE_PLY; - useSleepingThreads = Options["Use Sleeping Threads"]; size_t requested = Options["Threads"]; assert(requested > 0); - while (threads.size() < requested) - threads.push_back(new Thread(&Thread::idle_loop)); + while (size() < requested) + push_back(new Thread()); - while (threads.size() > requested) + while (size() > requested) { - delete threads.back(); - threads.pop_back(); + delete back(); + pop_back(); } } -// wake_up() is called before a new search to start the threads that are waiting -// on the sleep condition and to reset maxPly. When useSleepingThreads is set -// threads will be woken up at split time. +// slave_available() tries to find an idle thread which is available as a slave +// for the thread 'master'. -void ThreadPool::wake_up() const { +Thread* ThreadPool::available_slave(Thread* master) const { - for (size_t i = 0; i < threads.size(); i++) - { - threads[i]->maxPly = 0; - threads[i]->do_sleep = false; + for (const_iterator it = begin(); it != end(); ++it) + if ((*it)->is_available_to(master)) + return *it; - if (!useSleepingThreads) - threads[i]->wake_up(); - } -} - - -// sleep() is called after the search finishes to ask all the threads but the -// main one to go waiting on a sleep condition. - -void ThreadPool::sleep() const { - - // Main thread will go to sleep by itself to avoid a race with start_searching() - for (size_t i = 1; i < threads.size(); i++) - threads[i]->do_sleep = true; -} - - -// available_slave_exists() tries to find an idle thread which is available as -// a slave for the thread 'master'. - -bool ThreadPool::available_slave_exists(Thread* master) const { - - for (size_t i = 0; i < threads.size(); i++) - if (threads[i]->is_available_to(master)) - return true; - - return false; + return NULL; } // 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, or because we have no unused split -// point objects), 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 leave their idle loops and call -// search(). When all threads have returned from search() then split() returns. +// (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 +// leave their idle loops and call search(). When all threads have returned from +// search() then split() returns. template -Value ThreadPool::split(Position& pos, Stack* ss, Value alpha, Value beta, - Value bestValue, Move* bestMove, Depth depth, - Move threatMove, int moveCount, MovePicker* mp, int nodeType) { +void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bestValue, + Move* bestMove, Depth depth, Move threatMove, int moveCount, + MovePicker* movePicker, int nodeType) { assert(pos.pos_is_ok()); - assert(bestValue > -VALUE_INFINITE); - assert(bestValue <= alpha); - assert(alpha < beta); - assert(beta <= VALUE_INFINITE); - assert(depth > DEPTH_ZERO); - - Thread* master = pos.this_thread(); - - if (master->splitPointsCnt >= MAX_SPLITPOINTS_PER_THREAD) - return bestValue; + assert(*bestValue <= alpha && alpha < beta && beta <= VALUE_INFINITE); + assert(*bestValue > -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 - SplitPoint& sp = master->splitPoints[master->splitPointsCnt]; + SplitPoint& sp = splitPoints[splitPointsSize]; - sp.parent = master->curSplitPoint; - sp.master = master; - sp.cutoff = false; - sp.slavesMask = 1ULL << master->idx; + sp.masterThread = this; + sp.parentSplitPoint = activeSplitPoint; + sp.slavesMask = 1ULL << idx; sp.depth = depth; + sp.bestValue = *bestValue; sp.bestMove = *bestMove; sp.threatMove = threatMove; sp.alpha = alpha; sp.beta = beta; sp.nodeType = nodeType; - sp.bestValue = bestValue; - sp.mp = mp; + sp.movePicker = movePicker; sp.moveCount = moveCount; sp.pos = &pos; sp.nodes = 0; + sp.cutoff = false; sp.ss = ss; - assert(master->is_searching); - - master->curSplitPoint = &sp; - int slavesCnt = 0; - // Try to allocate available threads and ask them to start searching setting - // is_searching flag. This must be done under lock protection to avoid concurrent + // 'searching' flag. This must be done under lock protection to avoid concurrent // allocation of the same slave by another master. + Threads.mutex.lock(); sp.mutex.lock(); - mutex.lock(); - for (size_t i = 0; i < threads.size() && !Fake; ++i) - if (threads[i]->is_available_to(master)) - { - sp.slavesMask |= 1ULL << i; - threads[i]->curSplitPoint = &sp; - threads[i]->is_searching = true; // Slave leaves idle_loop() + splitPointsSize++; + activeSplitPoint = &sp; + activePosition = NULL; - if (useSleepingThreads) - threads[i]->wake_up(); + size_t slavesCnt = 1; // This thread is always included + Thread* slave; - if (++slavesCnt + 1 >= maxThreadsPerSplitPoint) // Master is always included - break; - } - - master->splitPointsCnt++; - - mutex.unlock(); - sp.mutex.unlock(); + while ( (slave = Threads.available_slave(this)) != NULL + && ++slavesCnt <= Threads.maxThreadsPerSplitPoint && !Fake) + { + sp.slavesMask |= 1ULL << slave->idx; + slave->activeSplitPoint = &sp; + slave->searching = true; // Slave leaves idle_loop() + slave->notify_one(); // Could be sleeping + } // Everything is set up. The master thread enters the idle loop, from which - // it will instantly launch a search, because its is_searching flag is set. + // it will instantly launch a search, because its 'searching' flag is set. // The thread will return from the idle loop when all slaves have finished // their work at this split point. - if (slavesCnt || Fake) + if (slavesCnt > 1 || Fake) { - master->idle_loop(); + sp.mutex.unlock(); + Threads.mutex.unlock(); + + Thread::idle_loop(); // Force a call to base class idle_loop() // In helpful master concept a master can help only a sub-tree of its split // point, and because here is all finished is not possible master is booked. - assert(!master->is_searching); + assert(!searching); + 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::is_available_to(). + Threads.mutex.lock(); + sp.mutex.lock(); } - // We have returned from the idle loop, which means that all threads are - // finished. Note that setting is_searching and decreasing splitPointsCnt is - // done under lock protection to avoid a race with Thread::is_available_to(). - sp.mutex.lock(); // To protect sp.nodes - mutex.lock(); - - master->is_searching = true; - master->splitPointsCnt--; - master->curSplitPoint = sp.parent; + searching = true; + splitPointsSize--; + activeSplitPoint = sp.parentSplitPoint; + activePosition = &pos; pos.set_nodes_searched(pos.nodes_searched() + sp.nodes); *bestMove = sp.bestMove; + *bestValue = sp.bestValue; - mutex.unlock(); sp.mutex.unlock(); - - return sp.bestValue; + Threads.mutex.unlock(); } // Explicit template instantiations -template Value ThreadPool::split(Position&, Stack*, Value, Value, Value, Move*, Depth, Move, int, MovePicker*, int); -template Value ThreadPool::split(Position&, Stack*, Value, Value, Value, Move*, Depth, Move, int, MovePicker*, int); +template void Thread::split(Position&, Stack*, Value, Value, Value*, Move*, Depth, Move, int, MovePicker*, int); +template void Thread::split< true>(Position&, Stack*, Value, Value, Value*, Move*, Depth, Move, int, MovePicker*, int); -// set_timer() is used to set the timer to trigger after msec milliseconds. -// If msec is 0 then timer is stopped. +// wait_for_think_finished() waits for main thread to go to sleep then returns -void ThreadPool::set_timer(int msec) { +void ThreadPool::wait_for_think_finished() { - timer->mutex.lock(); - timer->maxPly = msec; - timer->sleepCondition.notify_one(); // Wake up and restart the timer - timer->mutex.unlock(); -} - - -// wait_for_search_finished() waits for main thread to go to sleep, this means -// search is finished. Then returns. - -void ThreadPool::wait_for_search_finished() { - - Thread* t = main_thread(); + MainThread* t = main_thread(); t->mutex.lock(); - t->sleepCondition.notify_one(); // In case is waiting for stop or ponderhit - while (!t->do_sleep) sleepCondition.wait(t->mutex); + while (t->thinking) sleepCondition.wait(t->mutex); t->mutex.unlock(); } -// start_searching() wakes up the main thread sleeping in main_loop() so to start -// a new search, then returns immediately. +// start_thinking() wakes up the main thread sleeping in MainThread::idle_loop() +// so to start a new search, then returns immediately. -void ThreadPool::start_searching(const Position& pos, const LimitsType& limits, - const std::vector& searchMoves, StateStackPtr& states) { - wait_for_search_finished(); +void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits, + const std::vector& searchMoves, StateStackPtr& states) { + wait_for_think_finished(); SearchTime = Time::now(); // As early as possible Signals.stopOnPonderhit = Signals.firstRootMove = false; Signals.stop = Signals.failedLowAtRoot = false; - RootPosition = pos; + RootPos = pos; Limits = limits; SetupStates = states; // Ownership transfer here RootMoves.clear(); for (MoveList ml(pos); !ml.end(); ++ml) - if (searchMoves.empty() || std::count(searchMoves.begin(), searchMoves.end(), ml.move())) + if ( searchMoves.empty() + || std::count(searchMoves.begin(), searchMoves.end(), ml.move())) RootMoves.push_back(RootMove(ml.move())); - main_thread()->do_sleep = false; - main_thread()->wake_up(); + main_thread()->thinking = true; + main_thread()->notify_one(); // Starts main thread } diff --git a/DroidFish/jni/stockfish/thread.h b/DroidFish/jni/stockfish/thread.h index 38a29e8..fbd3b7f 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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,7 +28,7 @@ #include "position.h" #include "search.h" -const int MAX_THREADS = 32; +const int MAX_THREADS = 64; // Because SplitPoint::slavesMask is a uint64_t const int MAX_SPLITPOINTS_PER_THREAD = 8; struct Mutex { @@ -56,22 +56,22 @@ private: WaitCondition c; }; -class Thread; +struct Thread; struct SplitPoint { // Const data after split point has been setup const Position* pos; const Search::Stack* ss; + Thread* masterThread; Depth depth; Value beta; int nodeType; - Thread* master; Move threatMove; // Const pointers to shared data - MovePicker* mp; - SplitPoint* parent; + MovePicker* movePicker; + SplitPoint* parentSplitPoint; // Shared data Mutex mutex; @@ -90,77 +90,76 @@ struct SplitPoint { /// tables so that once we get a pointer to an entry its life time is unlimited /// and we don't have to care about someone changing the entry under our feet. -class Thread { +struct Thread { - typedef void (Thread::* Fn) (); // Pointer to member function + Thread(); + virtual ~Thread(); -public: - Thread(Fn fn); - ~Thread(); - - void wake_up(); + virtual void idle_loop(); + void notify_one(); bool cutoff_occurred() const; bool is_available_to(Thread* master) const; - void idle_loop(); - void main_loop(); - void timer_loop(); - void wait_for_stop_or_ponderhit(); + void wait_for(volatile const bool& b); + + template + void split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value* bestValue, Move* bestMove, + Depth depth, Move threatMove, int moveCount, MovePicker* movePicker, int nodeType); SplitPoint splitPoints[MAX_SPLITPOINTS_PER_THREAD]; - MaterialTable materialTable; - PawnTable pawnTable; + Material::Table materialTable; + Endgames endgames; + Pawns::Table pawnsTable; + Position* activePosition; size_t idx; int maxPly; Mutex mutex; ConditionVariable sleepCondition; NativeHandle handle; - Fn start_fn; - SplitPoint* volatile curSplitPoint; - volatile int splitPointsCnt; - volatile bool is_searching; - volatile bool do_sleep; - volatile bool do_exit; + SplitPoint* volatile activeSplitPoint; + volatile int splitPointsSize; + volatile bool searching; + volatile bool exit; }; -/// ThreadPool class handles all the threads related stuff like init, starting, +/// MainThread and TimerThread are sublassed from Thread to characterize the two +/// special threads: the main one and the recurring timer. + +struct MainThread : public Thread { + MainThread() : thinking(true) {} // Avoid a race with start_thinking() + virtual void idle_loop(); + volatile bool thinking; +}; + +struct TimerThread : public Thread { + TimerThread() : msec(0) {} + virtual void idle_loop(); + int msec; +}; + + +/// ThreadPool struct handles all the threads related stuff like init, starting, /// parking and, the most important, launching a slave thread at a split point. /// All the access to shared thread data is done through this class. -class ThreadPool { +struct ThreadPool : public std::vector { -public: void init(); // No c'tor and d'tor, threads rely on globals that should void exit(); // be initialized and valid during the whole thread lifetime. - Thread& operator[](size_t id) { return *threads[id]; } - bool use_sleeping_threads() const { return useSleepingThreads; } - int min_split_depth() const { return minimumSplitDepth; } - size_t size() const { return threads.size(); } - Thread* main_thread() { return threads[0]; } - - void wake_up() const; - void sleep() const; + MainThread* main_thread() { return static_cast((*this)[0]); } void read_uci_options(); - bool available_slave_exists(Thread* master) const; - void set_timer(int msec); - void wait_for_search_finished(); - void start_searching(const Position&, const Search::LimitsType&, - const std::vector&, Search::StateStackPtr&); + Thread* available_slave(Thread* master) const; + void wait_for_think_finished(); + void start_thinking(const Position&, const Search::LimitsType&, + const std::vector&, Search::StateStackPtr&); - template - Value split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value bestValue, Move* bestMove, - Depth depth, Move threatMove, int moveCount, MovePicker* mp, int nodeType); -private: - friend class Thread; - - std::vector threads; - Thread* timer; + bool sleepWhileIdle; + Depth minimumSplitDepth; + size_t maxThreadsPerSplitPoint; Mutex mutex; ConditionVariable sleepCondition; - Depth minimumSplitDepth; - int maxThreadsPerSplitPoint; - bool useSleepingThreads; + TimerThread* timer; }; extern ThreadPool Threads; diff --git a/DroidFish/jni/stockfish/timeman.cpp b/DroidFish/jni/stockfish/timeman.cpp index b8f8396..8d2bacc 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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,7 +29,7 @@ namespace { /// Constants const int MoveHorizon = 50; // Plan time management at most this many moves ahead - const float MaxRatio = 3.0f; // When in trouble, we can step over reserved time with this ratio + const float MaxRatio = 7.0f; // When in trouble, we can step over reserved time with this ratio const float StealRatio = 0.33f; // However we must not steal time from remaining moves over this ratio diff --git a/DroidFish/jni/stockfish/timeman.h b/DroidFish/jni/stockfish/timeman.h index ddf96c6..484513b 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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/tt.cpp b/DroidFish/jni/stockfish/tt.cpp index 9dbfcb5..998d737 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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,42 +25,32 @@ TranspositionTable TT; // Our global transposition table -TranspositionTable::TranspositionTable() { - - size = generation = 0; - entries = NULL; -} - -TranspositionTable::~TranspositionTable() { - - delete [] entries; -} - /// TranspositionTable::set_size() sets the size of the transposition table, -/// measured in megabytes. Transposition table consists of a power of 2 number of -/// TTCluster and each cluster consists of ClusterSize number of TTEntries. Each -/// non-empty entry contains information of exactly one position. +/// measured in megabytes. Transposition table consists of a power of 2 number +/// of clusters and each cluster consists of ClusterSize number of TTEntry. void TranspositionTable::set_size(size_t mbSize) { - size_t newSize = 1ULL << msb((mbSize << 20) / sizeof(TTCluster)); + assert(msb((mbSize << 20) / sizeof(TTEntry)) < 32); - if (newSize == size) + uint32_t size = ClusterSize << msb((mbSize << 20) / sizeof(TTEntry[ClusterSize])); + + if (hashMask == size - ClusterSize) return; - size = newSize; - delete [] entries; - entries = new (std::nothrow) TTCluster[size]; - - if (!entries) + free(mem); + mem = malloc(size * sizeof(TTEntry) + (CACHE_LINE_SIZE - 1)); + if (!mem) { std::cerr << "Failed to allocate " << mbSize << "MB for transposition table." << std::endl; exit(EXIT_FAILURE); } - clear(); // Operator new is not guaranteed to initialize memory to zero + table = (TTEntry*)((size_t(mem) + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1)); + hashMask = size - ClusterSize; + clear(); // Newly allocated block of memory is not initialized } @@ -70,7 +60,7 @@ void TranspositionTable::set_size(size_t mbSize) { void TranspositionTable::clear() { - memset(entries, 0, size * sizeof(TTCluster)); + memset(table, 0, (hashMask + ClusterSize) * sizeof(TTEntry)); } @@ -82,23 +72,23 @@ void TranspositionTable::clear() { /// 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 posKey, Value v, Bound t, Depth d, Move m, Value statV, Value kingD) { +void TranspositionTable::store(const Key key, Value v, Bound t, Depth d, Move m, Value statV, Value kingD) { int c1, c2, c3; TTEntry *tte, *replace; - uint32_t posKey32 = posKey >> 32; // Use the high 32 bits as key inside the cluster + uint32_t key32 = key >> 32; // Use the high 32 bits as key inside the cluster - tte = replace = first_entry(posKey); + tte = replace = first_entry(key); - for (int i = 0; i < ClusterSize; i++, tte++) + for (unsigned i = 0; i < ClusterSize; i++, tte++) { - if (!tte->key() || tte->key() == posKey32) // Empty or overwrite old + if (!tte->key() || tte->key() == key32) // Empty or overwrite old { // Preserve any existing ttMove if (m == MOVE_NONE) m = tte->move(); - tte->save(posKey32, v, t, d, m, generation, statV, kingD); + tte->save(key32, v, t, d, m, generation, statV, kingD); return; } @@ -110,7 +100,7 @@ void TranspositionTable::store(const Key posKey, Value v, Bound t, Depth d, Move if (c1 + c2 + c3 > 0) replace = tte; } - replace->save(posKey32, v, t, d, m, generation, statV, kingD); + replace->save(key32, v, t, d, m, generation, statV, kingD); } @@ -118,24 +108,14 @@ void TranspositionTable::store(const Key posKey, Value v, Bound t, Depth d, Move /// transposition table. Returns a pointer to the TTEntry or NULL if /// position is not found. -TTEntry* TranspositionTable::probe(const Key posKey) const { +TTEntry* TranspositionTable::probe(const Key key) const { - uint32_t posKey32 = posKey >> 32; - TTEntry* tte = first_entry(posKey); + TTEntry* tte = first_entry(key); + uint32_t key32 = key >> 32; - for (int i = 0; i < ClusterSize; i++, tte++) - if (tte->key() == posKey32) + for (unsigned i = 0; i < ClusterSize; i++, tte++) + if (tte->key() == key32) return tte; return NULL; } - - -/// TranspositionTable::new_search() is called at the beginning of every new -/// search. It increments the "generation" variable, which is used to -/// distinguish transposition table entries from previous searches from -/// entries from the current search. - -void TranspositionTable::new_search() { - generation++; -} diff --git a/DroidFish/jni/stockfish/tt.h b/DroidFish/jni/stockfish/tt.h index 3edd5e8..1caa277 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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 @@ -44,7 +44,7 @@ class TTEntry { public: - void save(uint32_t k, Value v, Bound b, Depth d, Move m, int g, Value statV, Value statM) { + void save(uint32_t k, Value v, Bound b, Depth d, Move m, int g, Value ev, Value em) { key32 = (uint32_t)k; move16 = (uint16_t)m; @@ -52,63 +52,53 @@ public: generation8 = (uint8_t)g; value16 = (int16_t)v; depth16 = (int16_t)d; - staticValue = (int16_t)statV; - staticMargin = (int16_t)statM; + evalValue = (int16_t)ev; + evalMargin = (int16_t)em; } void set_generation(int g) { generation8 = (uint8_t)g; } - uint32_t key() const { return key32; } - Depth depth() const { return (Depth)depth16; } - Move move() const { return (Move)move16; } - Value value() const { return (Value)value16; } - Bound type() const { return (Bound)bound; } - int generation() const { return (int)generation8; } - Value static_value() const { return (Value)staticValue; } - Value static_value_margin() const { return (Value)staticMargin; } + uint32_t key() const { return key32; } + Depth depth() const { return (Depth)depth16; } + Move move() const { return (Move)move16; } + Value value() const { return (Value)value16; } + Bound type() const { return (Bound)bound; } + int generation() const { return (int)generation8; } + Value eval_value() const { return (Value)evalValue; } + Value eval_margin() const { return (Value)evalMargin; } private: uint32_t key32; uint16_t move16; uint8_t bound, generation8; - int16_t value16, depth16, staticValue, staticMargin; + int16_t value16, depth16, evalValue, evalMargin; }; -/// This is the number of TTEntry slots for each cluster -const int ClusterSize = 4; - - -/// TTCluster consists of ClusterSize number of TTEntries. Size of TTCluster -/// must not be bigger than a cache line size. In case it is less, it should -/// be padded to guarantee always aligned accesses. - -struct TTCluster { - TTEntry data[ClusterSize]; -}; - - -/// The transposition table class. This is basically just a huge array containing -/// TTCluster objects, and a few methods for writing and reading entries. +/// A TranspositionTable consists of a power of 2 number of clusters and each +/// cluster consists of ClusterSize number of TTEntry. Each non-empty entry +/// contains information of exactly one position. Size of a cluster shall not be +/// bigger than a cache line size. In case it is less, it should be padded to +/// guarantee always aligned accesses. class TranspositionTable { - TranspositionTable(const TranspositionTable&); - TranspositionTable& operator=(const TranspositionTable&); + static const unsigned ClusterSize = 4; // A cluster is 64 Bytes public: - TranspositionTable(); - ~TranspositionTable(); + ~TranspositionTable() { free(mem); } + void new_search() { generation++; } + + TTEntry* probe(const Key key) const; + TTEntry* first_entry(const Key key) const; + void refresh(const TTEntry* tte) const; void set_size(size_t mbSize); void clear(); - void store(const Key posKey, Value v, Bound type, Depth d, Move m, Value statV, Value kingD); - TTEntry* probe(const Key posKey) const; - void new_search(); - TTEntry* first_entry(const Key posKey) const; - void refresh(const TTEntry* tte) const; + void store(const Key key, Value v, Bound type, Depth d, Move m, Value statV, Value kingD); private: - size_t size; - TTCluster* entries; + uint32_t hashMask; + TTEntry* table; + void* mem; uint8_t generation; // Size must be not bigger then TTEntry::generation8 }; @@ -119,9 +109,9 @@ extern TranspositionTable TT; /// a cluster given a position. The lowest order bits of the key are used to /// get the index of the cluster. -inline TTEntry* TranspositionTable::first_entry(const Key posKey) const { +inline TTEntry* TranspositionTable::first_entry(const Key key) const { - return entries[((uint32_t)posKey) & (size - 1)].data; + return table + ((uint32_t)key & hashMask); } diff --git a/DroidFish/jni/stockfish/types.h b/DroidFish/jni/stockfish/types.h index ce73cab..dae86db 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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 @@ -35,13 +35,14 @@ /// | only in 64-bit mode. For compiling requires hardware with /// | popcnt support. +#include #include #include #include #include "platform.h" -#if defined(_WIN64) +#if defined(_WIN64) && !defined(IS_64BIT) # include // MSVC popcnt and bsfq instrinsics # define IS_64BIT # define USE_BSFQ @@ -51,10 +52,15 @@ # 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 + +#define CACHE_LINE_SIZE 64 #if defined(_MSC_VER) || defined(__INTEL_COMPILER) -# define CACHE_LINE_ALIGNMENT __declspec(align(64)) +# define CACHE_LINE_ALIGNMENT __declspec(align(CACHE_LINE_SIZE)) #else -# define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(64))) +# define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(CACHE_LINE_SIZE))) #endif #if defined(_MSC_VER) @@ -132,12 +138,20 @@ enum CastleRight { // Defined as in PolyGlot book hash key WHITE_OOO = 2, BLACK_OO = 4, BLACK_OOO = 8, - ALL_CASTLES = 15 + ALL_CASTLES = 15, + CASTLE_RIGHT_NB = 16 }; enum CastlingSide { KING_SIDE, - QUEEN_SIDE + QUEEN_SIDE, + CASTLING_SIDE_NB = 2 +}; + +enum Phase { + PHASE_ENDGAME = 0, + PHASE_MIDGAME = 128, + MG = 0, EG = 1, PHASE_NB = 2 }; enum ScaleFactor { @@ -168,8 +182,6 @@ enum Value { VALUE_ENSURE_INTEGER_SIZE_P = INT_MAX, VALUE_ENSURE_INTEGER_SIZE_N = INT_MIN, - Mg = 0, Eg = 1, - PawnValueMg = 198, PawnValueEg = 258, KnightValueMg = 817, KnightValueEg = 846, BishopValueMg = 836, BishopValueEg = 857, @@ -179,17 +191,19 @@ enum Value { enum PieceType { NO_PIECE_TYPE = 0, ALL_PIECES = 0, - PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6 + PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6, + PIECE_TYPE_NB = 8 }; enum Piece { - NO_PIECE = 16, // color_of(NO_PIECE) == NO_COLOR + NO_PIECE = 0, W_PAWN = 1, W_KNIGHT = 2, W_BISHOP = 3, W_ROOK = 4, W_QUEEN = 5, W_KING = 6, - B_PAWN = 9, B_KNIGHT = 10, B_BISHOP = 11, B_ROOK = 12, B_QUEEN = 13, B_KING = 14 + B_PAWN = 9, B_KNIGHT = 10, B_BISHOP = 11, B_ROOK = 12, B_QUEEN = 13, B_KING = 14, + PIECE_NB = 16 }; enum Color { - WHITE, BLACK, NO_COLOR + WHITE, BLACK, NO_COLOR, COLOR_NB = 2 }; enum Depth { @@ -215,6 +229,8 @@ enum Square { SQ_A8, SQ_B8, SQ_C8, SQ_D8, SQ_E8, SQ_F8, SQ_G8, SQ_H8, SQ_NONE, + SQUARE_NB = 64, + DELTA_N = 8, DELTA_E = 1, DELTA_S = -8, @@ -229,11 +245,11 @@ enum Square { }; enum File { - FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H + FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, FILE_NB = 8 }; enum Rank { - RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8 + RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB = 8 }; @@ -320,9 +336,9 @@ inline Score apply_weight(Score v, Score w) { namespace Zobrist { - extern Key psq[2][8][64]; // [color][pieceType][square / piece count] - extern Key enpassant[8]; // [file] - extern Key castle[16]; // [castleRight] + extern Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; + extern Key enpassant[FILE_NB]; + extern Key castle[CASTLE_RIGHT_NB]; extern Key side; extern Key exclusion; @@ -331,9 +347,9 @@ namespace Zobrist { CACHE_LINE_ALIGNMENT -extern Score pieceSquareTable[16][64]; // [piece][square] -extern Value PieceValue[2][18]; // [Mg / Eg][piece / pieceType] -extern int SquareDistance[64][64]; // [square][square] +extern Score pieceSquareTable[PIECE_NB][SQUARE_NB]; +extern Value PieceValue[PHASE_NB][PIECE_NB]; +extern int SquareDistance[SQUARE_NB][SQUARE_NB]; struct MoveStack { Move move; @@ -377,6 +393,7 @@ inline PieceType type_of(Piece p) { } inline Color color_of(Piece p) { + assert(p != NO_PIECE); return Color(p >> 3); } @@ -425,12 +442,12 @@ inline int square_distance(Square s1, Square s2) { return SquareDistance[s1][s2]; } -inline char file_to_char(File f) { - return char(f - FILE_A + int('a')); +inline char file_to_char(File f, bool tolower = true) { + return char(f - FILE_A + (tolower ? 'a' : 'A')); } inline char rank_to_char(Rank r) { - return char(r - RANK_1 + int('1')); + return char(r - RANK_1 + '1'); } inline Square pawn_push(Color c) { @@ -459,7 +476,7 @@ inline Move make_move(Square from, Square to) { template inline Move make(Square from, Square to, PieceType pt = KNIGHT) { - return Move(to | (from << 6) | T | ((pt - KNIGHT) << 12)) ; + return Move(to | (from << 6) | T | ((pt - KNIGHT) << 12)); } inline bool is_ok(Move m) { @@ -473,21 +490,4 @@ inline const std::string square_to_string(Square s) { return ch; } -/// Our insertion sort implementation, works with pointers and iterators and is -/// guaranteed to be stable, as is needed. -template -void sort(K first, K last) -{ - T tmp; - K p, q; - - for (p = first + 1; p < last; p++) - { - tmp = *p; - for (q = p; q != first && *(q-1) < tmp; --q) - *q = *(q-1); - *q = tmp; - } -} - #endif // !defined(TYPES_H_INCLUDED) diff --git a/DroidFish/jni/stockfish/uci.cpp b/DroidFish/jni/stockfish/uci.cpp index 3895c7a..1707923 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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,6 +17,7 @@ along with this program. If not, see . */ +#include #include #include #include @@ -43,7 +44,7 @@ namespace { void set_option(istringstream& up); void set_position(Position& pos, istringstream& up); - void go(Position& pos, istringstream& up); + void go(const Position& pos, istringstream& up); } @@ -55,78 +56,32 @@ namespace { void UCI::loop(const string& args) { Position pos(StartFEN, false, Threads.main_thread()); // The root position - string cmd, token; + string token, cmd = args; - while (token != "quit") - { - if (!args.empty()) - cmd = args; - - else if (!getline(cin, cmd)) // Block here waiting for input + do { + if (args.empty() && !getline(cin, cmd)) // Block here waiting for input cmd = "quit"; istringstream is(cmd); is >> skipws >> token; - if (token == "quit" || token == "stop") + if (token == "quit" || token == "stop" || token == "ponderhit") { - Search::Signals.stop = true; - Threads.wait_for_search_finished(); // Cannot quit while threads are running - } - - else if (token == "ponderhit") - { - // The opponent has played the expected move. GUI sends "ponderhit" if - // we were told to ponder on the same move the opponent has played. We - // should continue searching but switching from pondering to normal search. - Search::Limits.ponder = false; - - if (Search::Signals.stopOnPonderhit) + // 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 != "ponderhit" || Search::Signals.stopOnPonderhit) { Search::Signals.stop = true; - Threads.main_thread()->wake_up(); // Could be sleeping + Threads.main_thread()->notify_one(); // Could be sleeping } + else + Search::Limits.ponder = false; } - - else if (token == "go") - go(pos, is); - - else if (token == "ucinewgame") - { /* Avoid returning "Unknown command" */ } - - else if (token == "isready") - sync_cout << "readyok" << sync_endl; - - else if (token == "position") - set_position(pos, is); - - else if (token == "setoption") - set_option(is); - - else if (token == "d") - pos.print(); - - else if (token == "flip") - pos.flip(); - - else if (token == "eval") - sync_cout << Eval::trace(pos) << sync_endl; - - else if (token == "bench") - benchmark(pos, is); - - else if (token == "key") - sync_cout << "key: " << hex << pos.key() - << "\nmaterial key: " << pos.material_key() - << "\npawn key: " << pos.pawn_key() << sync_endl; - - else if (token == "uci") - sync_cout << "id name " << engine_info(true) - << "\n" << Options - << "\nuciok" << sync_endl; - - else if (token == "perft" && (is >> token)) // Read depth + else if (token == "perft" && (is >> token)) // Read perft depth { stringstream ss; @@ -135,16 +90,33 @@ void UCI::loop(const string& args) { 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 << sync_endl; + else if (token == "uci") + sync_cout << "id name " << engine_info(true) + << "\n" << Options + << "\nuciok" << sync_endl; + + else if (token == "ucinewgame") { /* Avoid returning "Unknown command" */ } + else if (token == "go") go(pos, is); + else if (token == "position") set_position(pos, is); + else if (token == "setoption") set_option(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; - if (!args.empty()) // Command line arguments have one-shot behaviour - { - Threads.wait_for_search_finished(); - break; - } - } + } while (token != "quit" && args.empty()); // Args have one-shot behaviour + + Threads.wait_for_think_finished(); // Cannot quit while search is running } @@ -173,7 +145,7 @@ namespace { else return; - pos.from_fen(fen, Options["UCI_Chess960"], Threads.main_thread()); + pos.set(fen, Options["UCI_Chess960"], Threads.main_thread()); SetupStates = Search::StateStackPtr(new std::stack()); // Parse move list (if any) @@ -210,10 +182,10 @@ 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 then starts + // the thinking time and other parameters from the input string, and starts // the search. - void go(Position& pos, istringstream& is) { + void go(const Position& pos, istringstream& is) { Search::LimitsType limits; vector searchMoves; @@ -221,31 +193,23 @@ namespace { while (is >> token) { - if (token == "wtime") - is >> limits.time[WHITE]; - else if (token == "btime") - is >> limits.time[BLACK]; - else if (token == "winc") - is >> limits.inc[WHITE]; - else if (token == "binc") - is >> limits.inc[BLACK]; - else if (token == "movestogo") - is >> limits.movestogo; - else if (token == "depth") - is >> limits.depth; - else if (token == "nodes") - is >> limits.nodes; - else if (token == "movetime") - is >> limits.movetime; - else if (token == "infinite") - limits.infinite = true; - else if (token == "ponder") - limits.ponder = true; - else if (token == "searchmoves") + if (token == "searchmoves") while (is >> token) searchMoves.push_back(move_from_uci(pos, token)); + + else if (token == "wtime") is >> limits.time[WHITE]; + else if (token == "btime") is >> limits.time[BLACK]; + else if (token == "winc") is >> limits.inc[WHITE]; + else if (token == "binc") is >> limits.inc[BLACK]; + else if (token == "movestogo") is >> limits.movestogo; + else if (token == "depth") is >> limits.depth; + else if (token == "nodes") is >> limits.nodes; + else if (token == "movetime") is >> limits.movetime; + else if (token == "mate") is >> limits.mate; + else if (token == "infinite") limits.infinite = true; + else if (token == "ponder") limits.ponder = true; } - Threads.start_searching(pos, limits, searchMoves, SetupStates); + Threads.start_thinking(pos, limits, searchMoves, SetupStates); } } diff --git a/DroidFish/jni/stockfish/ucioption.cpp b/DroidFish/jni/stockfish/ucioption.cpp index bbbbbbf..4bb4369 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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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 @@ -64,6 +64,7 @@ void init(OptionsMap& o) { o["Search Log Filename"] = Option("SearchLog.txt"); o["Book File"] = Option("book.bin"); o["Best Book Move"] = Option(false); + o["Contempt Factor"] = Option(0, -50, 50); o["Mobility (Middle Game)"] = Option(100, 0, 200, on_eval); o["Mobility (Endgame)"] = Option(100, 0, 200, on_eval); o["Passed Pawns (Middle Game)"] = Option(100, 0, 200, on_eval); @@ -71,11 +72,11 @@ void init(OptionsMap& o) { o["Space"] = Option(100, 0, 200, on_eval); o["Aggressiveness"] = Option(100, 0, 200, on_eval); o["Cowardice"] = Option(100, 0, 200, on_eval); - o["Min Split Depth"] = Option(msd, 4, 7, on_threads); + o["Min Split Depth"] = Option(msd, 4, 12, on_threads); o["Max Threads per Split Point"] = Option(5, 4, 8, on_threads); o["Threads"] = Option(cpus, 1, MAX_THREADS, on_threads); - o["Use Sleeping Threads"] = Option(true, on_threads); - o["Hash"] = Option(32, 4, 8192, on_hash_size); + o["Use Sleeping Threads"] = Option(true); + o["Hash"] = Option(32, 1, 8192, on_hash_size); o["Clear Hash"] = Option(on_clear_hash); o["Ponder"] = Option(true); o["OwnBook"] = Option(false); diff --git a/DroidFish/jni/stockfish/ucioption.h b/DroidFish/jni/stockfish/ucioption.h index df8797f..feafe36 100644 --- a/DroidFish/jni/stockfish/ucioption.h +++ b/DroidFish/jni/stockfish/ucioption.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-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 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