Skip to content

Commit c5b4946

Browse files
committed
feat: quiet move history
Added quiet move history. A quick test estimates an Elo level of roughly 1650 on CCRL scale.
1 parent 610b213 commit c5b4946

File tree

5 files changed

+62
-144
lines changed

5 files changed

+62
-144
lines changed

config.json

Lines changed: 0 additions & 136 deletions
This file was deleted.

src/history.h

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
#include <cstring>
66
#include <vector>
77

8-
constexpr short MAX_HISTORY_SCORE = 5000;
9-
constexpr short MIN_HISTORY_SCORE = -5000;
8+
constexpr short MAX_HISTORY_SCORE = 10000;
9+
constexpr short MIN_HISTORY_SCORE = -10000;
1010

1111
/**
1212
* This class keeps track of the search history.
@@ -40,8 +40,25 @@ class SearchHistory {
4040
}
4141
};
4242

43+
struct QuietHistoryTable {
44+
int16_t data[2][64][64];
45+
46+
void clear() { std::memset(data, 0, sizeof(data)); }
47+
48+
inline int16_t get(const Color stm, const Move& move) {
49+
return data[(int) stm][move.from().index()][move.to().index()];
50+
}
51+
inline void update(const Color stm, const Move& move, int16_t bonus) {
52+
int16_t& ref = data[(int) stm][move.from().index()][move.to().index()];
53+
int newScore = bonus + ref;
54+
55+
ref = std::clamp(newScore, (int) MIN_HISTORY_SCORE, (int) MAX_HISTORY_SCORE);
56+
}
57+
};
58+
4359
public:
44-
KillerTable killerTable[MAX_PLY];
60+
KillerTable killerTable[MAX_PLY];
61+
QuietHistoryTable qHistoryTable;
4562

4663
void clear() {
4764
for (int i = 0; i < MAX_PLY; i++) {

src/movepick.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,11 @@ void MovePicker::generateQuietMoves() {
209209
if (pos.at(move.from()).type() != PieceType::PAWN &&
210210
(attacks::pawn(pos.sideToMove(), move.to()) &
211211
pos.pieces(PieceType::PAWN, ~pos.sideToMove()))) {
212-
score -= 200;
212+
score -= 900;
213213
}
214+
// Assign score from quiet history
215+
int historyScore = history.qHistoryTable.get(pos.sideToMove(), move);
216+
score += historyScore / 8;
214217

215218
quietBuffer.emplace_back(ScoredMove {move.move(), score});
216219
}

src/movepick.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,6 @@ class MovePicker {
6161

6262
Move next();
6363
void skipQuiet();
64+
65+
const MovePickerStage& getStage() const { return stage; }
6466
};

src/search.cpp

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ Value qsearch(Position& pos, int depth, int ply, Value alpha, Value beta) {
103103
break;
104104
}
105105

106+
// Delta Pruning
107+
if (!pos.inCheck() && standPat + Value(1000) < alpha) {
108+
continue;
109+
}
110+
106111
pos.makeMove(m);
107112
Value score = -qsearch(pos, depth - 1, ply + 1, -beta, -alpha);
108113
pos.unmakeMove(m);
@@ -166,6 +171,9 @@ Value negamax(Position& pos, int depth, int ply, Value alpha, Value beta, bool c
166171

167172
searchHistory.killerTable[ply + 1].clear();
168173
searchStats.nodes++;
174+
if (ply == 0) {
175+
searchHistory.qHistoryTable.clear();
176+
}
169177

170178
// Transposition table lookup
171179
// See if this node has been visited before. If so, we can reuse the data
@@ -271,9 +279,10 @@ Value negamax(Position& pos, int depth, int ply, Value alpha, Value beta, bool c
271279
currSS->bestMove = m;
272280
if (score >= beta) {
273281
ttFlag = EntryType::LOWER_BOUND;
274-
// Record quiet killer moves
282+
// Update quiet history
275283
if (!pos.isCapture(bestMove)) {
276284
searchHistory.killerTable[ply].add(bestMove);
285+
searchHistory.qHistoryTable.update(pos.sideToMove(), bestMove, depth * depth);
277286
}
278287
break;
279288
}
@@ -303,15 +312,36 @@ void searchWorker(SearchParams params, Position pos) {
303312
Move rootBestMove;
304313
Value rootBestScore = MATED_VALUE;
305314

315+
Value windowUpper = 20;
316+
Value windowLower = 20;
317+
306318
for (int depth = 1; depth <= maxDepth; ++depth) {
307319
if (g_stopRequested.load())
308320
break;
309321
if (g_timeControl.hitSoftLimit(depth, (int) searchStats.nodes, 0))
310322
break;
311323

312-
Value score = negamax<true>(pos, depth, 0, MATED_VALUE, MATE_VALUE, false);
313-
if (g_stopRequested.load())
314-
break;
324+
// Aspiration window
325+
Value score;
326+
if (depth <= 3) {
327+
score = negamax<true>(pos, depth, 0, MATED_VALUE, MATE_VALUE, false);
328+
} else {
329+
Value alpha = rootBestScore - windowLower;
330+
Value beta = rootBestScore + windowUpper;
331+
score = negamax<true>(pos, depth, 0, alpha, beta, false);
332+
// Adjust window on fail-highs or fail-lows
333+
if (score >= beta) {
334+
windowUpper = std::min(MATE_VALUE, windowUpper * 2);
335+
continue;
336+
} else if (score <= alpha) {
337+
windowLower = std::min(MATE_VALUE, windowLower * 2);
338+
continue;
339+
} else {
340+
// Score within window, accept the score
341+
windowUpper = 25;
342+
windowLower = 25;
343+
}
344+
}
315345

316346
auto pv = extractPv(pos, depth);
317347
if (!pv.empty())
@@ -326,6 +356,8 @@ void searchWorker(SearchParams params, Position pos) {
326356

327357
if (g_timeControl.hitSoftLimit(depth, (int) searchStats.nodes, 0))
328358
break;
359+
if (g_stopRequested.load())
360+
break;
329361
}
330362

331363
if (rootBestMove.move() != 0)

0 commit comments

Comments
 (0)