From: Marco Costalba Date: Sat, 3 Mar 2018 10:29:29 +0000 (+0100) Subject: Merge Stats tables X-Git-Url: https://git.sesse.net/?p=stockfish;a=commitdiff_plain;h=f35e52f030af837ed8a89eecd67a6f746ee2e897 Merge Stats tables Use a recursive std::array with variadic template parameters to get rid of the last redundacy. The first template T parameter is the base type of the array, the W parameter is the weight applied to the bonuses when we update values with the << operator, the D parameter limits the range of updates (range is [-W * D, W * D]), and the last parameters (Size and Sizes) encode the dimensions of the array. This allows greater flexibility because we can now tweak the range [-W * D, W * D] for each table. Patch removes more lines than what adds and streamlines the Stats soup in movepick.h Closes PR#1422 and PR#1421 No functional change. --- diff --git a/src/movepick.cpp b/src/movepick.cpp index 35f9d25e..a71d307e 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -127,7 +127,7 @@ void MovePicker::score() { for (auto& m : *this) if (Type == CAPTURES) m.value = PieceValue[MG][pos.piece_on(to_sq(m))] - + Value((*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))]); + + (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))]; else if (Type == QUIETS) m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] diff --git a/src/movepick.h b/src/movepick.h index 0aba61d0..d9f0457a 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -23,44 +23,33 @@ #include #include +#include #include "movegen.h" #include "position.h" #include "types.h" -/// StatBoards is a generic 2-dimensional array used to store various statistics -template -struct StatBoards : public std::array, Size1> { +/// StatsEntry stores the stat table value. It is usually a number but could +/// be a move or even a nested history. We use a class instead of naked value +/// to directly call history update operator<<() on the entry so to use stats +/// tables at caller sites as simple multi-dim arrays. +template +class StatsEntry { - void fill(const T& v) { - T* p = &(*this)[0][0]; - std::fill(p, p + sizeof(*this) / sizeof(*p), v); - } - - void update(T& entry, int bonus, const int D) { - - assert(abs(bonus) <= D); // Ensure range is [-32 * D, 32 * D] - assert(abs(32 * D) < (std::numeric_limits::max)()); // Ensure we don't overflow - - entry += bonus * 32 - entry * abs(bonus) / D; - - assert(abs(entry) <= 32 * D); - } -}; + static const bool IsInt = std::is_integral::value; + typedef typename std::conditional::type TT; -/// StatCubes is a generic 3-dimensional array used to store various statistics -template -struct StatCubes : public std::array, Size2>, Size1> { + T entry; - void fill(const T& v) { - T* p = &(*this)[0][0][0]; - std::fill(p, p + sizeof(*this) / sizeof(*p), v); - } +public: + T* get() { return &entry; } + void operator=(const T& v) { entry = v; } + operator TT() const { return entry; } - void update(T& entry, int bonus, const int D, const int W) { + void operator<<(int bonus) { assert(abs(bonus) <= D); // Ensure range is [-W * D, W * D] - assert(abs(W * D) < (std::numeric_limits::max)()); // Ensure we don't overflow + assert(abs(W * D) < std::numeric_limits::max()); // Ensure we don't overflow entry += bonus * W - entry * abs(bonus) / D; @@ -68,50 +57,50 @@ struct StatCubes : public std::array, Size2>, Si } }; -/// ButterflyBoards are 2 tables (one for each color) indexed by the move's from -/// and to squares, see chessprogramming.wikispaces.com/Butterfly+Boards -typedef StatBoards ButterflyBoards; - -/// PieceToBoards are addressed by a move's [piece][to] information -typedef StatBoards PieceToBoards; +/// Stats is a generic N-dimensional array used to store various statistics. +/// The first template T parameter is the base type of the array, the W parameter +/// is the weight applied to the bonuses when we update values with the << operator, +/// the D parameter limits the range of updates (range is [-W * D, W * D]), and +/// the last parameters (Size and Sizes) encode the dimensions of the array. +template +struct Stats : public std::array, Size> +{ + T* get() { return this->at(0).get(); } -/// CapturePieceToBoards are addressed by a move's [piece][to][captured piece type] information -typedef StatCubes CapturePieceToBoards; - -/// ButterflyHistory records how often quiet moves have been successful or -/// unsuccessful during the current search, and is used for reduction and move -/// ordering decisions. It uses ButterflyBoards as backing store. -struct ButterflyHistory : public ButterflyBoards { - - void update(Color c, Move m, int bonus) { - StatBoards::update((*this)[c][from_to(m)], bonus, 324); + void fill(const T& v) { + T* p = get(); + std::fill(p, p + sizeof(*this) / sizeof(*p), v); } }; -/// PieceToHistory is like ButterflyHistory, but is based on PieceToBoards -struct PieceToHistory : public PieceToBoards { - - void update(Piece pc, Square to, int bonus) { - StatBoards::update((*this)[pc][to], bonus, 936); - } +template +struct Stats : public std::array, Size> { + T* get() { return this->at(0).get(); } }; -/// CapturePieceToHistory is like PieceToHistory, but is based on CapturePieceToBoards -struct CapturePieceToHistory : public CapturePieceToBoards { +/// Different tables use different W/D parameter, name them to ease readibility +enum StatsParams { W2 = 2, W32 = 32, D324 = 324, D936 = 936, NOT_USED = 0 }; - void update(Piece pc, Square to, PieceType captured, int bonus) { - StatCubes::update((*this)[pc][to][captured], bonus, 324, 2); - } -}; +/// ButterflyHistory records how often quiet moves have been successful or +/// unsuccessful during the current search, and is used for reduction and move +/// ordering decisions. It uses 2 tables (one for each color) indexed by +/// the move's from and to squares, see chessprogramming.wikispaces.com/Butterfly+Boards +typedef Stats ButterflyHistory; /// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous /// move, see chessprogramming.wikispaces.com/Countermove+Heuristic -typedef StatBoards CounterMoveHistory; +typedef Stats CounterMoveHistory; + +/// CapturePieceToHistory is addressed by a move's [piece][to][captured piece type] +typedef Stats CapturePieceToHistory; + +/// PieceToHistory is like ButterflyHistory but is addressed by a move's [piece][to] +typedef Stats PieceToHistory; -/// ContinuationHistory is the history of a given pair of moves, usually the -/// current one given a previous one. History table is based on PieceToBoards -/// instead of ButterflyBoards. -typedef StatBoards ContinuationHistory; +/// ContinuationHistory is the combined history of a given pair of moves, usually +/// the current one given a previous one. The nested history table is based on +/// PieceToHistory instead of ButterflyBoards. +typedef Stats ContinuationHistory; /// MovePicker class is used to pick one pseudo legal move at a time from the diff --git a/src/search.cpp b/src/search.cpp index 2acca99c..f6cf8de8 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -289,7 +289,7 @@ void Thread::search() { std::memset(ss-4, 0, 7 * sizeof(Stack)); for (int i = 4; i > 0; i--) - (ss-i)->contHistory = &this->contHistory[NO_PIECE][0]; // Use as sentinel + (ss-i)->contHistory = this->contHistory[NO_PIECE][0].get(); // Use as sentinel bestValue = delta = alpha = -VALUE_INFINITE; beta = VALUE_INFINITE; @@ -550,7 +550,7 @@ namespace { (ss+1)->ply = ss->ply + 1; ss->currentMove = (ss+1)->excludedMove = bestMove = MOVE_NONE; - ss->contHistory = &thisThread->contHistory[NO_PIECE][0]; + ss->contHistory = thisThread->contHistory[NO_PIECE][0].get(); (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; Square prevSq = to_sq((ss-1)->currentMove); @@ -595,7 +595,7 @@ namespace { else if (!pos.capture_or_promotion(ttMove)) { int penalty = -stat_bonus(depth); - thisThread->mainHistory.update(pos.side_to_move(), ttMove, penalty); + thisThread->mainHistory[pos.side_to_move()][from_to(ttMove)] << penalty; update_continuation_histories(ss, pos.moved_piece(ttMove), to_sq(ttMove), penalty); } } @@ -715,7 +715,7 @@ namespace { Depth R = ((823 + 67 * depth / ONE_PLY) / 256 + std::min((eval - beta) / PawnValueMg, 3)) * ONE_PLY; ss->currentMove = MOVE_NULL; - ss->contHistory = &thisThread->contHistory[NO_PIECE][0]; + ss->contHistory = thisThread->contHistory[NO_PIECE][0].get(); pos.do_null_move(st); Value nullValue = depth-R < ONE_PLY ? -qsearch(pos, ss+1, -beta, -beta+1) @@ -762,7 +762,7 @@ namespace { if (pos.legal(move)) { ss->currentMove = move; - ss->contHistory = &thisThread->contHistory[pos.moved_piece(move)][to_sq(move)]; + ss->contHistory = thisThread->contHistory[pos.moved_piece(move)][to_sq(move)].get(); assert(depth >= 5 * ONE_PLY); @@ -937,7 +937,7 @@ moves_loop: // When in check, search starts from here // Update the current move (this must be done after singular extension search) ss->currentMove = move; - ss->contHistory = &thisThread->contHistory[movedPiece][to_sq(move)]; + ss->contHistory = thisThread->contHistory[movedPiece][to_sq(move)].get(); // Step 15. Make the move pos.do_move(move, st, givesCheck); @@ -1401,7 +1401,7 @@ moves_loop: // When in check, search starts from here for (int i : {1, 2, 4}) if (is_ok((ss-i)->currentMove)) - (ss-i)->contHistory->update(pc, to, bonus); + (*(ss-i)->contHistory)[pc][to] << bonus; } @@ -1413,14 +1413,14 @@ moves_loop: // When in check, search starts from here CapturePieceToHistory& captureHistory = pos.this_thread()->captureHistory; Piece moved_piece = pos.moved_piece(move); PieceType captured = type_of(pos.piece_on(to_sq(move))); - captureHistory.update(moved_piece, to_sq(move), captured, bonus); + captureHistory[moved_piece][to_sq(move)][captured] << bonus; // Decrease all the other played capture moves for (int i = 0; i < captureCnt; ++i) { moved_piece = pos.moved_piece(captures[i]); captured = type_of(pos.piece_on(to_sq(captures[i]))); - captureHistory.update(moved_piece, to_sq(captures[i]), captured, -bonus); + captureHistory[moved_piece][to_sq(captures[i])][captured] << -bonus; } } @@ -1438,7 +1438,7 @@ moves_loop: // When in check, search starts from here Color us = pos.side_to_move(); Thread* thisThread = pos.this_thread(); - thisThread->mainHistory.update(us, move, bonus); + thisThread->mainHistory[us][from_to(move)] << bonus; update_continuation_histories(ss, pos.moved_piece(move), to_sq(move), bonus); if (is_ok((ss-1)->currentMove)) @@ -1450,7 +1450,7 @@ moves_loop: // When in check, search starts from here // Decrease all the other played quiet moves for (int i = 0; i < quietsCnt; ++i) { - thisThread->mainHistory.update(us, quiets[i], -bonus); + thisThread->mainHistory[us][from_to(quiets[i])] << -bonus; update_continuation_histories(ss, pos.moved_piece(quiets[i]), to_sq(quiets[i]), -bonus); } } diff --git a/src/thread.cpp b/src/thread.cpp index 6516192d..ec62c3ff 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -62,9 +62,9 @@ void Thread::clear() { for (auto& to : contHistory) for (auto& h : to) - h.fill(0); + h.get()->fill(0); - contHistory[NO_PIECE][0].fill(Search::CounterMovePruneThreshold - 1); + contHistory[NO_PIECE][0].get()->fill(Search::CounterMovePruneThreshold - 1); } /// Thread::start_searching() wakes up the thread that will start the search