Skip to content

Commit eaea7da

Browse files
committed
fix: incomplete search corrupts performance
1 parent 8f64c55 commit eaea7da

File tree

2 files changed

+47
-17
lines changed

2 files changed

+47
-17
lines changed

README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@ This engine is still **WIP** and will be updated from time to time. Currently I
88

99
## Compiling
1010

11-
I currently have a small build script that compiles the engine with `g++` (`build.py`). Why, you may ask, don't you use `Makefile`? Because I don't know how to use it yet... (What a shame) I will probably move to `Makefile` in the future.
12-
13-
Nevertheless, I have compiled the binary for Windows and you can find it in the release page.
11+
Use the Makefile to compile the engine.
1412

1513
## License
1614

src/search.cpp

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ struct Scratchpad {
3636
Value staticEval = Value::none();
3737
uint16_t currentMove = 0;
3838
uint16_t bestMove = 0;
39+
uint16_t pvIndex = 0;
3940
bool nullMoveAllowed = true;
4041
};
4142

@@ -52,26 +53,27 @@ struct SearchResult {
5253
uint32_t nodes = 0;
5354
uint16_t time = 0;
5455
uint16_t bestmove = 0;
56+
const uint16_t *pvTable = nullptr;
5557

5658
static SearchResult from(const SearchStatistics &stats,
5759
const TimeControl &tc, Value bestValue,
58-
Move bestMove) {
60+
Move bestMove, const uint16_t *pvTable) {
5961
SearchResult item;
6062
item.depth = stats.depth;
6163
item.seldepth = stats.seldepth;
6264
item.nodes = stats.nodes;
6365
item.score = bestValue;
6466
item.bestmove = bestMove.move();
6567
item.time = tc._elapsed();
68+
item.pvTable = pvTable;
6669
return item;
6770
}
6871

6972
void print() const {
70-
DEBUG("1.5");
7173
std::cout << "info depth " << depth << " score " << score << " nodes "
7274
<< nodes << " seldepth " << seldepth << " time " << time
73-
<< " pv " << chess::uci::moveToUci(Move(bestmove))
74-
<< std::endl;
75+
<< " pv " << Move(bestmove);
76+
std::cout << std::endl;
7577
}
7678
};
7779

@@ -93,6 +95,9 @@ class Searcher {
9395
TimeControl tc;
9496

9597
bool searchAborted = false;
98+
bool searchInterrupted = false;
99+
100+
uint16_t pvTable[(MAX_PLY * MAX_PLY + MAX_PLY) / 2]; // triangular array
96101

97102
public:
98103
Searcher(const Position &pos) : pos(pos) {
@@ -107,22 +112,26 @@ class Searcher {
107112
for (size_t i = 0; i < MAX_PLY; ++i) {
108113
stack[i] = Scratchpad();
109114
}
115+
searchInterrupted = false;
110116

111117
this->tc = timeControl;
112118

113119
MovePicker mp(pos);
120+
mp.init();
114121

115122
// Handle no legal move
116123
if (mp.size() == 0) {
117-
result = SearchResult::from(stats, tc, DRAW_VALUE, Move::NO_MOVE);
124+
result = SearchResult::from(stats, tc, DRAW_VALUE, Move::NO_MOVE,
125+
pvTable);
118126
std::cerr << "info string no legal moves" << std::endl;
119127
return;
120128
}
121129
// We don't have to waste time searching if there is only one reply in
122130
// competition
123131
if (tc.competitionMode && mp.size() == 1) {
124132
Value staticEval = evaluate(pos);
125-
result = SearchResult::from(stats, tc, staticEval, mp.pick());
133+
result =
134+
SearchResult::from(stats, tc, staticEval, mp.pick(), pvTable);
126135
return;
127136
}
128137

@@ -138,16 +147,17 @@ class Searcher {
138147
const Value score =
139148
negamax<PVNode>(alpha, beta, stats.depth, 0, false);
140149
DEBUG("search finished");
141-
assert(bestEvalCurr == score);
142-
if (bestMoveCurr.isValid()) {
150+
151+
if (bestMoveCurr.isValid() && !searchInterrupted) {
143152
DEBUG("0");
144153
// Update evaluation stability statistics
145154
history.updateStability(bestEvalRoot, bestEvalCurr);
146155
bestMoveRoot = bestMoveCurr;
147156
bestEvalRoot = bestEvalCurr;
148157
DEBUG("1");
149158
// Output the information about this iteration
150-
SearchResult::from(stats, tc, bestEvalCurr, bestMoveRoot.move())
159+
SearchResult::from(stats, tc, bestEvalCurr, bestMoveRoot.move(),
160+
pvTable)
151161
.print();
152162
DEBUG("2");
153163
}
@@ -161,11 +171,13 @@ class Searcher {
161171
stats.depth++;
162172
}
163173

164-
// If we failed to find even one move, return the first legal move
165174
if (!bestMoveRoot.isValid()) {
175+
std::cout << "info string no move was found\n";
166176
bestMoveRoot = mp.pick();
167177
}
168-
result = SearchResult::from(stats, tc, bestEvalRoot, bestMoveRoot);
178+
179+
result =
180+
SearchResult::from(stats, tc, bestEvalRoot, bestMoveRoot, pvTable);
169181
}
170182

171183
/**
@@ -211,6 +223,10 @@ class Searcher {
211223

212224
history.killerTable[ply + 1].clear();
213225

226+
const uint16_t pvIndex = stack[ply].pvIndex;
227+
pvTable[pvIndex] = Move::NO_MOVE;
228+
stack[ply + 1].pvIndex = pvIndex + MAX_PLY - ply;
229+
214230
/**
215231
* Transposition Table Probing.
216232
*
@@ -331,6 +347,7 @@ class Searcher {
331347
pos.unmakeMove(m);
332348

333349
if (hasReachedHardLimit()) {
350+
searchInterrupted = true;
334351
return alpha;
335352
}
336353

@@ -345,9 +362,19 @@ class Searcher {
345362
alpha = score;
346363
ttEntryType = EntryType::EXACT;
347364

365+
if (isPVNode && ply > 0) {
366+
// New PV node, update PV table
367+
pvTable[pvIndex] = m.move();
368+
uint16_t n = MAX_PLY - ply - 1;
369+
uint16_t *target = pvTable + pvIndex + 1;
370+
uint16_t *source = pvTable + stack[ply + 1].pvIndex;
371+
while (n-- && (*target++ = *source++))
372+
;
373+
}
374+
348375
if (ply == 0) { // Root node
349-
bestEvalCurr = bestValue;
350-
bestMoveCurr = bestMove;
376+
bestEvalCurr = score;
377+
bestMoveCurr = m;
351378
}
352379

353380
if (score >= beta) {
@@ -363,7 +390,9 @@ class Searcher {
363390
return Value::matedIn(ply);
364391
}
365392

366-
tt.store(pos, ttEntryType, depth, bestMove, bestValue);
393+
if (!eligibleTTPrune) {
394+
tt.store(pos, ttEntryType, depth, bestMove, bestValue);
395+
}
367396
return bestValue;
368397
}
369398

@@ -384,6 +413,7 @@ class Searcher {
384413
<< ")");
385414
// If running out of time, return immediately
386415
if (hasReachedHardLimit()) {
416+
searchInterrupted = true;
387417
return alpha;
388418
}
389419
// Draw detection
@@ -536,6 +566,8 @@ void think(SearchParams params, const Position pos) {
536566

537567
std::cout << "bestmove " << chess::uci::moveToUci(Move(result.bestmove))
538568
<< std::endl;
569+
std::cout << std::flush;
570+
539571
delete searcher;
540572
searcher = nullptr; // release
541573
}

0 commit comments

Comments
 (0)