template<>
void MovePicker::score<QUIETS>() {
- const HistoryStats& history = pos.this_thread()->history;
+ const ButterflyHistory& history = pos.this_thread()->history;
- const CounterMoveStats& cmh = *(ss-1)->counterMoves;
- const CounterMoveStats& fmh = *(ss-2)->counterMoves;
- const CounterMoveStats& fm2 = *(ss-4)->counterMoves;
+ const PieceToHistory& cmh = *(ss-1)->history;
+ const PieceToHistory& fmh = *(ss-2)->history;
+ const PieceToHistory& fm2 = *(ss-4)->history;
Color c = pos.side_to_move();
m.value = cmh[pos.moved_piece(m)][to_sq(m)]
+ fmh[pos.moved_piece(m)][to_sq(m)]
+ fm2[pos.moved_piece(m)][to_sq(m)]
- + history.get(c, m);
+ + history[c][from_to(m)];
}
template<>
void MovePicker::score<EVASIONS>() {
// Try captures ordered by MVV/LVA, then non-captures ordered by stats heuristics
- const HistoryStats& history = pos.this_thread()->history;
+ const ButterflyHistory& history = pos.this_thread()->history;
Color c = pos.side_to_move();
for (auto& m : *this)
if (pos.capture(m))
m.value = PieceValue[MG][pos.piece_on(to_sq(m))]
- - Value(type_of(pos.moved_piece(m))) + HistoryStats::Max;
+ - Value(type_of(pos.moved_piece(m))) + (1 << 28);
else
- m.value = history.get(c, m);
+ m.value = history[c][from_to(m)];
}
#ifndef MOVEPICK_H_INCLUDED
#define MOVEPICK_H_INCLUDED
-#include <cstring> // For std::memset
+#include <array>
#include "movegen.h"
#include "position.h"
#include "types.h"
+/// StatBoards is a generic 2-dimensional array used to store various statistics
+template<int Size1, int Size2, typename T = int>
+struct StatBoards : public std::array<std::array<T, Size2>, Size1> {
-/// HistoryStats records how often quiet moves have been successful or unsuccessful
-/// during the current search, and is used for reduction and move ordering decisions.
-struct HistoryStats {
+ void fill(const T& v) {
+ T* p = &(*this)[0][0];
+ std::fill(p, p + sizeof(*this) / sizeof(*p), v);
+ }
+};
- static const int Max = 1 << 28;
+/// 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<COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyBoards;
- int get(Color c, Move m) const { return table[c][from_sq(m)][to_sq(m)]; }
- void clear() { std::memset(table, 0, sizeof(table)); }
- void update(Color c, Move m, int v) {
+/// PieceToBoards are addressed by a move's [piece][to] information
+typedef StatBoards<PIECE_NB, SQUARE_NB> PieceToBoards;
- Square from = from_sq(m);
- Square to = to_sq(m);
+/// 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 v) {
const int D = 324;
+ int& entry = (*this)[c][from_to(m)];
assert(abs(v) <= D); // Consistency check for below formula
- table[c][from][to] -= table[c][from][to] * abs(v) / D;
- table[c][from][to] += v * 32;
- }
+ entry += v * 32 - entry * abs(v) / D;
-private:
- int table[COLOR_NB][SQUARE_NB][SQUARE_NB];
+ assert(abs(entry) <= 32 * D);
+ }
};
+/// PieceToHistory is like ButterflyHistory, but is based on PieceToBoards
+struct PieceToHistory : public PieceToBoards {
-/// A template struct, used to generate MoveStats and CounterMoveHistoryStats:
-/// MoveStats store the move that refute a previous one.
-/// CounterMoveHistoryStats is like HistoryStats, but with two consecutive moves.
-/// Entries are stored using only the moving piece and destination square, hence
-/// two moves with different origin but same destination and piece will be
-/// considered identical.
-template<typename T>
-struct Stats {
- const T* operator[](Piece pc) const { return table[pc]; }
- T* operator[](Piece pc) { return table[pc]; }
- void clear() { std::memset(table, 0, sizeof(table)); }
- void update(Piece pc, Square to, Move m) { table[pc][to] = m; }
void update(Piece pc, Square to, int v) {
const int D = 936;
+ int& entry = (*this)[pc][to];
assert(abs(v) <= D); // Consistency check for below formula
- table[pc][to] -= table[pc][to] * abs(v) / D;
- table[pc][to] += v * 32;
- }
+ entry += v * 32 - entry * abs(v) / D;
-private:
- T table[PIECE_NB][SQUARE_NB];
+ assert(abs(entry) <= 32 * D);
+ }
};
-typedef Stats<Move> MoveStats;
-typedef Stats<int> CounterMoveStats;
-typedef Stats<CounterMoveStats> CounterMoveHistoryStats;
+/// CounterMoveStat stores counter moves indexed by [piece][to] of the previous
+/// move, see chessprogramming.wikispaces.com/Countermove+Heuristic
+typedef StatBoards<PIECE_NB, SQUARE_NB, Move> CounterMoveStat;
+
+/// CounterMoveHistoryStat is like CounterMoveStat but instead of a move it
+/// stores a full history (based on PieceTo boards instead of ButterflyBoards).
+typedef StatBoards<PIECE_NB, SQUARE_NB, PieceToHistory> CounterMoveHistoryStat;
/// MovePicker class is used to pick one pseudo legal move at a time from the
for (Thread* th : Threads)
{
- th->counterMoves.clear();
- th->history.clear();
- th->counterMoveHistory.clear();
th->resetCalls = true;
+ th->counterMoves.fill(MOVE_NONE);
+ th->history.fill(0);
- CounterMoveStats& cm = th->counterMoveHistory[NO_PIECE][0];
- auto* t = &cm[NO_PIECE][0];
- std::fill(t, t + sizeof(cm)/sizeof(*t), CounterMovePruneThreshold - 1);
+ for (auto& to : th->counterMoveHistory)
+ for (auto& h : to)
+ h.fill(0);
+
+ th->counterMoveHistory[NO_PIECE][0].fill(CounterMovePruneThreshold - 1);
}
Threads.main()->previousScore = VALUE_INFINITE;
std::memset(ss-4, 0, 7 * sizeof(Stack));
for(int i = 4; i > 0; i--)
- (ss-i)->counterMoves = &this->counterMoveHistory[NO_PIECE][0]; // Use as sentinel
+ (ss-i)->history = &this->counterMoveHistory[NO_PIECE][0]; // Use as sentinel
bestValue = delta = alpha = -VALUE_INFINITE;
beta = VALUE_INFINITE;
Thread* thisThread = pos.this_thread();
inCheck = pos.checkers();
moveCount = quietCount = ss->moveCount = 0;
- ss->history = 0;
+ ss->statScore = 0;
bestValue = -VALUE_INFINITE;
ss->ply = (ss-1)->ply + 1;
assert(0 <= ss->ply && ss->ply < MAX_PLY);
ss->currentMove = (ss+1)->excludedMove = bestMove = MOVE_NONE;
- ss->counterMoves = &thisThread->counterMoveHistory[NO_PIECE][0];
+ ss->history = &thisThread->counterMoveHistory[NO_PIECE][0];
(ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE;
Square prevSq = to_sq((ss-1)->currentMove);
Depth R = ((823 + 67 * depth / ONE_PLY) / 256 + std::min((eval - beta) / PawnValueMg, 3)) * ONE_PLY;
ss->currentMove = MOVE_NULL;
- ss->counterMoves = &thisThread->counterMoveHistory[NO_PIECE][0];
+ ss->history = &thisThread->counterMoveHistory[NO_PIECE][0];
pos.do_null_move(st);
Value nullValue = depth-R < ONE_PLY ? -qsearch<NonPV, false>(pos, ss+1, -beta, -beta+1)
if (pos.legal(move))
{
ss->currentMove = move;
- ss->counterMoves = &thisThread->counterMoveHistory[pos.moved_piece(move)][to_sq(move)];
+ ss->history = &thisThread->counterMoveHistory[pos.moved_piece(move)][to_sq(move)];
pos.do_move(move, st);
value = -search<NonPV>(pos, ss+1, -rbeta, -rbeta+1, rdepth, !cutNode, false);
moves_loop: // When in check search starts from here
- const CounterMoveStats& cmh = *(ss-1)->counterMoves;
- const CounterMoveStats& fmh = *(ss-2)->counterMoves;
- const CounterMoveStats& fm2 = *(ss-4)->counterMoves;
+ const PieceToHistory& cmh = *(ss-1)->history;
+ const PieceToHistory& fmh = *(ss-2)->history;
+ const PieceToHistory& fm2 = *(ss-4)->history;
MovePicker mp(pos, ttMove, depth, ss);
value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc
// Update the current move (this must be done after singular extension search)
ss->currentMove = move;
- ss->counterMoves = &thisThread->counterMoveHistory[moved_piece][to_sq(move)];
+ ss->history = &thisThread->counterMoveHistory[moved_piece][to_sq(move)];
// Step 14. Make the move
pos.do_move(move, st, givesCheck);
&& !pos.see_ge(make_move(to_sq(move), from_sq(move))))
r -= 2 * ONE_PLY;
- ss->history = cmh[moved_piece][to_sq(move)]
- + fmh[moved_piece][to_sq(move)]
- + fm2[moved_piece][to_sq(move)]
- + thisThread->history.get(~pos.side_to_move(), move)
- - 4000; // Correction factor
+ ss->statScore = cmh[moved_piece][to_sq(move)]
+ + fmh[moved_piece][to_sq(move)]
+ + fm2[moved_piece][to_sq(move)]
+ + thisThread->history[~pos.side_to_move()][from_to(move)]
+ - 4000; // Correction factor
// Decrease/increase reduction by comparing opponent's stat score
- if (ss->history > 0 && (ss-1)->history < 0)
+ if (ss->statScore > 0 && (ss-1)->statScore < 0)
r -= ONE_PLY;
- else if (ss->history < 0 && (ss-1)->history > 0)
+ else if (ss->statScore < 0 && (ss-1)->statScore > 0)
r += ONE_PLY;
// Decrease/increase reduction for moves with a good/bad history
- r = std::max(DEPTH_ZERO, (r / ONE_PLY - ss->history / 20000) * ONE_PLY);
+ r = std::max(DEPTH_ZERO, (r / ONE_PLY - ss->statScore / 20000) * ONE_PLY);
}
Depth d = std::max(newDepth - r, ONE_PLY);
for (int i : {1, 2, 4})
if (is_ok((ss-i)->currentMove))
- (ss-i)->counterMoves->update(pc, s, bonus);
+ (ss-i)->history->update(pc, s, bonus);
}
if (is_ok((ss-1)->currentMove))
{
Square prevSq = to_sq((ss-1)->currentMove);
- thisThread->counterMoves.update(pos.piece_on(prevSq), prevSq, move);
+ thisThread->counterMoves[pos.piece_on(prevSq)][prevSq]=move;
}
// Decrease all the other played quiet moves
struct Stack {
Move* pv;
- CounterMoveStats* counterMoves;
+ PieceToHistory* history;
int ply;
Move currentMove;
Move excludedMove;
Move killers[2];
Value staticEval;
- int history;
+ int statScore;
int moveCount;
};
Depth rootDepth;
Depth completedDepth;
std::atomic_bool resetCalls;
- MoveStats counterMoves;
- HistoryStats history;
- CounterMoveHistoryStats counterMoveHistory;
+ CounterMoveStat counterMoves;
+ ButterflyHistory history;
+ CounterMoveHistoryStat counterMoveHistory;
};
return Square(m & 0x3F);
}
+inline int from_to(Move m) {
+ return m & 0xFFF;
+}
+
inline MoveType type_of(Move m) {
return MoveType(m & (3 << 14));
}