@@ -36,6 +36,7 @@ struct Scratchpad {
36
36
Value staticEval = Value::none();
37
37
uint16_t currentMove = 0 ;
38
38
uint16_t bestMove = 0 ;
39
+ uint16_t pvIndex = 0 ;
39
40
bool nullMoveAllowed = true ;
40
41
};
41
42
@@ -52,26 +53,27 @@ struct SearchResult {
52
53
uint32_t nodes = 0 ;
53
54
uint16_t time = 0 ;
54
55
uint16_t bestmove = 0 ;
56
+ const uint16_t *pvTable = nullptr ;
55
57
56
58
static SearchResult from (const SearchStatistics &stats,
57
59
const TimeControl &tc, Value bestValue,
58
- Move bestMove) {
60
+ Move bestMove, const uint16_t *pvTable ) {
59
61
SearchResult item;
60
62
item.depth = stats.depth ;
61
63
item.seldepth = stats.seldepth ;
62
64
item.nodes = stats.nodes ;
63
65
item.score = bestValue;
64
66
item.bestmove = bestMove.move ();
65
67
item.time = tc._elapsed ();
68
+ item.pvTable = pvTable;
66
69
return item;
67
70
}
68
71
69
72
void print () const {
70
- DEBUG (" 1.5" );
71
73
std::cout << " info depth " << depth << " score " << score << " nodes "
72
74
<< nodes << " seldepth " << seldepth << " time " << time
73
- << " pv " << chess::uci::moveToUci ( Move (bestmove))
74
- << std::endl;
75
+ << " pv " << Move (bestmove);
76
+ std::cout << std::endl;
75
77
}
76
78
};
77
79
@@ -93,6 +95,9 @@ class Searcher {
93
95
TimeControl tc;
94
96
95
97
bool searchAborted = false ;
98
+ bool searchInterrupted = false ;
99
+
100
+ uint16_t pvTable[(MAX_PLY * MAX_PLY + MAX_PLY) / 2 ]; // triangular array
96
101
97
102
public:
98
103
Searcher (const Position &pos) : pos(pos) {
@@ -107,22 +112,26 @@ class Searcher {
107
112
for (size_t i = 0 ; i < MAX_PLY; ++i) {
108
113
stack[i] = Scratchpad ();
109
114
}
115
+ searchInterrupted = false ;
110
116
111
117
this ->tc = timeControl;
112
118
113
119
MovePicker mp (pos);
120
+ mp.init ();
114
121
115
122
// Handle no legal move
116
123
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);
118
126
std::cerr << " info string no legal moves" << std::endl;
119
127
return ;
120
128
}
121
129
// We don't have to waste time searching if there is only one reply in
122
130
// competition
123
131
if (tc.competitionMode && mp.size () == 1 ) {
124
132
Value staticEval = evaluate (pos);
125
- result = SearchResult::from (stats, tc, staticEval, mp.pick ());
133
+ result =
134
+ SearchResult::from (stats, tc, staticEval, mp.pick (), pvTable);
126
135
return ;
127
136
}
128
137
@@ -138,16 +147,17 @@ class Searcher {
138
147
const Value score =
139
148
negamax<PVNode>(alpha, beta, stats.depth , 0 , false );
140
149
DEBUG (" search finished" );
141
- assert (bestEvalCurr == score);
142
- if (bestMoveCurr.isValid ()) {
150
+
151
+ if (bestMoveCurr.isValid () && !searchInterrupted ) {
143
152
DEBUG (" 0" );
144
153
// Update evaluation stability statistics
145
154
history.updateStability (bestEvalRoot, bestEvalCurr);
146
155
bestMoveRoot = bestMoveCurr;
147
156
bestEvalRoot = bestEvalCurr;
148
157
DEBUG (" 1" );
149
158
// Output the information about this iteration
150
- SearchResult::from (stats, tc, bestEvalCurr, bestMoveRoot.move ())
159
+ SearchResult::from (stats, tc, bestEvalCurr, bestMoveRoot.move (),
160
+ pvTable)
151
161
.print ();
152
162
DEBUG (" 2" );
153
163
}
@@ -161,11 +171,13 @@ class Searcher {
161
171
stats.depth ++;
162
172
}
163
173
164
- // If we failed to find even one move, return the first legal move
165
174
if (!bestMoveRoot.isValid ()) {
175
+ std::cout << " info string no move was found\n " ;
166
176
bestMoveRoot = mp.pick ();
167
177
}
168
- result = SearchResult::from (stats, tc, bestEvalRoot, bestMoveRoot);
178
+
179
+ result =
180
+ SearchResult::from (stats, tc, bestEvalRoot, bestMoveRoot, pvTable);
169
181
}
170
182
171
183
/* *
@@ -211,6 +223,10 @@ class Searcher {
211
223
212
224
history.killerTable [ply + 1 ].clear ();
213
225
226
+ const uint16_t pvIndex = stack[ply].pvIndex ;
227
+ pvTable[pvIndex] = Move::NO_MOVE;
228
+ stack[ply + 1 ].pvIndex = pvIndex + MAX_PLY - ply;
229
+
214
230
/* *
215
231
* Transposition Table Probing.
216
232
*
@@ -331,6 +347,7 @@ class Searcher {
331
347
pos.unmakeMove (m);
332
348
333
349
if (hasReachedHardLimit ()) {
350
+ searchInterrupted = true ;
334
351
return alpha;
335
352
}
336
353
@@ -345,9 +362,19 @@ class Searcher {
345
362
alpha = score;
346
363
ttEntryType = EntryType::EXACT;
347
364
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
+
348
375
if (ply == 0 ) { // Root node
349
- bestEvalCurr = bestValue ;
350
- bestMoveCurr = bestMove ;
376
+ bestEvalCurr = score ;
377
+ bestMoveCurr = m ;
351
378
}
352
379
353
380
if (score >= beta) {
@@ -363,7 +390,9 @@ class Searcher {
363
390
return Value::matedIn (ply);
364
391
}
365
392
366
- tt.store (pos, ttEntryType, depth, bestMove, bestValue);
393
+ if (!eligibleTTPrune) {
394
+ tt.store (pos, ttEntryType, depth, bestMove, bestValue);
395
+ }
367
396
return bestValue;
368
397
}
369
398
@@ -384,6 +413,7 @@ class Searcher {
384
413
<< " )" );
385
414
// If running out of time, return immediately
386
415
if (hasReachedHardLimit ()) {
416
+ searchInterrupted = true ;
387
417
return alpha;
388
418
}
389
419
// Draw detection
@@ -536,6 +566,8 @@ void think(SearchParams params, const Position pos) {
536
566
537
567
std::cout << " bestmove " << chess::uci::moveToUci (Move (result.bestmove ))
538
568
<< std::endl;
569
+ std::cout << std::flush;
570
+
539
571
delete searcher;
540
572
searcher = nullptr ; // release
541
573
}
0 commit comments