Rev 33 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 33 | Rev 108 | ||
---|---|---|---|
Line 1... | Line 1... | ||
1 | #include "chess.h" |
1 | #include "chess.h" |
2 | #include "data.h" |
2 | #include "data.h" |
3 | /* last modified |
3 | /* last modified 01/10/16 */ |
4 | /* |
4 | /* |
5 | ******************************************************************************* |
5 | ******************************************************************************* |
6 | * * |
6 | * * |
7 | * Quiece() is the recursive routine used to implement the quiescence * |
7 | * Quiece() is the recursive routine used to implement the quiescence * |
8 | * search part of the alpha/beta negamax search. It performs the following * |
8 | * search part of the alpha/beta negamax search. It performs the following * |
Line 13... | Line 13... | ||
13 | * the current static evaluation, or else it may try captures and/or * |
13 | * the current static evaluation, or else it may try captures and/or * |
14 | * checking moves to see if it can improve the stand-pat score by making a * |
14 | * checking moves to see if it can improve the stand-pat score by making a * |
15 | * move that leads to some sort of positional or material gain. * |
15 | * move that leads to some sort of positional or material gain. * |
16 | * * |
16 | * * |
17 | * (2) The first phase is to generate all possible capture moves and then * |
17 | * (2) The first phase is to generate all possible capture moves and then * |
18 | * sort them into descending using the value |
18 | * sort them into descending using the value of the captured piece and the * |
19 | * * |
- | |
20 | * |
19 | * complemented value of the capturing piece. This is the classic MVV/LVA * |
21 | * * |
- | |
22 | * |
20 | * ordering approach that removes heavy pieces first in an attempt to reduce * |
23 | * first in an attempt to reduce the size of the sub-tree these pieces * |
- | |
24 | * |
21 | * the size of the sub-tree these pieces produce. * |
25 | * * |
22 | * * |
26 | * (3) When we get ready to actually search each capture, we analyze each * |
23 | * (3) When we get ready to actually search each capture, we analyze each * |
27 | * move to see if it appears reasonable. Small pieces can capture larger * |
24 | * move to see if it appears reasonable. Small pieces can capture larger * |
28 | * ones safely, ditto for equal exchanges. For the rest, we use |
25 | * ones safely, ditto for equal exchanges. For the rest, we use SEE() to * |
29 | * compute the SEE score. If this is less than zero, we do not search this * |
26 | * compute the SEE score. If this is less than zero, we do not search this * |
30 | * move at all to avoid wasting time, since a losing capture rarely helps * |
27 | * move at all to avoid wasting time, since a losing capture rarely helps * |
31 | * improve the score in the q-search. The goal here is to find a capture * |
28 | * improve the score in the q-search. The goal here is to find a capture * |
32 | * that improves on the stand-pat score and gets us closer to a position * |
29 | * that improves on the stand-pat score and gets us closer to a position * |
33 | * that we would describe as "quiet" or "static". * |
30 | * that we would describe as "quiet" or "static". * |
34 | * * |
31 | * * |
35 | * (4) If the parameter "checks" is non- |
32 | * (4) If the parameter "checks" is non-zero, then after searching the * |
36 | * captures, we generate checking moves and search those. This value also * |
33 | * captures, we generate checking moves and search those. This value also * |
37 | * slightly changes the way captures are searched, since those that are also * |
34 | * slightly changes the way captures are searched, since those that are also * |
38 | * checks result in calling QuiesceEvasions() which evades checks to see if * |
35 | * checks result in calling QuiesceEvasions() which evades checks to see if * |
39 | * the check is actually a mate. This means that we have one layer of full- * |
36 | * the check is actually a mate. This means that we have one layer of full- * |
40 | * width search to escape checks and do not allow a stand-pat which would * |
37 | * width search to escape checks and do not allow a stand-pat which would * |
41 | * hide the effect of the check completely. * |
38 | * hide the effect of the check completely. * |
42 | * * |
39 | * * |
43 | ******************************************************************************* |
40 | ******************************************************************************* |
44 | */ |
41 | */ |
45 | int Quiesce(TREE * RESTRICT tree, int |
42 | int Quiesce(TREE * RESTRICT tree, int ply, int wtm, int alpha, int beta, |
46 | int checks) { |
43 | int checks) { |
- | 44 | unsigned *next, *movep; |
|
47 | int original_alpha = alpha, value; |
45 | int original_alpha = alpha, value; |
48 | int *next; |
- | |
49 | int *movep, *sortv; |
- | |
50 | 46 | ||
51 | /* |
47 | /* |
52 | ************************************************************ |
48 | ************************************************************ |
53 | * * |
49 | * * |
54 | * Initialize. * |
50 | * Initialize. * |
Line 56... | Line 52... | ||
56 | ************************************************************ |
52 | ************************************************************ |
57 | */ |
53 | */ |
58 | if (ply >= MAXPLY - 1) |
54 | if (ply >= MAXPLY - 1) |
59 | return beta; |
55 | return beta; |
60 | #if defined(NODES) |
56 | #if defined(NODES) |
61 | if (--temp_search_nodes <= 0) { |
57 | if (search_nodes && --temp_search_nodes <= 0) { |
62 | abort_search = 1; |
58 | abort_search = 1; |
63 | return 0; |
59 | return 0; |
64 | } |
60 | } |
65 | #endif |
61 | #endif |
66 | if (tree->thread_id == 0) |
62 | if (tree->thread_id == 0) |
Line 99... | Line 95... | ||
99 | * save the path to this node as it is the PV that leads * |
95 | * save the path to this node as it is the PV that leads * |
100 | * to this score. * |
96 | * to this score. * |
101 | * * |
97 | * * |
102 | ************************************************************ |
98 | ************************************************************ |
103 | */ |
99 | */ |
104 | tree->curmv[ply] = 0; |
- | |
105 | value = Evaluate(tree, ply, wtm, alpha, beta); |
100 | value = Evaluate(tree, ply, wtm, alpha, beta); |
106 | #if defined(TRACE) |
101 | #if defined(TRACE) |
107 | if (ply <= trace_level) |
102 | if (ply <= trace_level) |
108 | Trace(tree, ply, value, wtm, alpha, beta, "Quiesce", EVALUATION); |
103 | Trace(tree, ply, value, wtm, alpha, beta, "Quiesce", serial, EVALUATION, 0); |
109 | #endif |
104 | #endif |
110 | if (value > alpha) { |
105 | if (value > alpha) { |
111 | if (value >= beta) |
106 | if (value >= beta) |
112 | return value; |
107 | return value; |
113 | alpha = value; |
108 | alpha = value; |
114 | tree->pv[ply].pathl = ply; |
109 | tree->pv[ply].pathl = ply; |
115 | tree->pv[ply].pathh = 0; |
110 | tree->pv[ply].pathh = 0; |
116 | tree->pv[ply].pathd = |
111 | tree->pv[ply].pathd = iteration; |
117 | } |
112 | } |
118 | /* |
113 | /* |
119 | ************************************************************ |
114 | ************************************************************ |
120 | * * |
115 | * * |
121 | * Generate captures and sort them based on simple MVV/LVA * |
116 | * Generate captures and sort them based on simple MVV/LVA * |
Line 124... | Line 119... | ||
124 | * possible, to get rid of heavy pieces quickly and reduce * |
119 | * possible, to get rid of heavy pieces quickly and reduce * |
125 | * the overall size of the tree. * |
120 | * the overall size of the tree. * |
126 | * * |
121 | * * |
127 | * Note that later we use the value of the capturing * |
122 | * Note that later we use the value of the capturing * |
128 | * piece, the value of the captured piece, and possibly * |
123 | * piece, the value of the captured piece, and possibly * |
129 | * |
124 | * SEE() to exclude captures that appear to lose material, * |
130 | * |
125 | * but we delay expending this effort as long as possible, * |
131 | * |
126 | * since beta cutoffs make it unnecessary to search all of * |
132 | * |
127 | * these moves anyway. * |
133 | * * |
128 | * * |
134 | ************************************************************ |
129 | ************************************************************ |
135 | */ |
130 | */ |
136 | tree->last[ply] = GenerateCaptures(tree, ply, wtm, tree->last[ply - 1]); |
131 | tree->last[ply] = GenerateCaptures(tree, ply, wtm, tree->last[ply - 1]); |
137 | sortv = tree->sort_value; |
- | |
138 | for (movep = tree->last[ply - 1]; movep < tree->last[ply]; movep++) { |
132 | for (movep = tree->last[ply - 1]; movep < tree->last[ply]; movep++) { |
139 | if (Captured(*movep) == king) |
133 | if (Captured(*movep) == king) |
140 | return beta; |
134 | return beta; |
141 | * |
135 | *movep += MVV_LVA[Captured(*movep)][Piece(*movep)]; |
142 | } |
136 | } |
143 | if (!checks && tree->last[ply] == tree->last[ply - 1]) { |
137 | if (!checks && tree->last[ply] == tree->last[ply - 1]) { |
144 | if (alpha != original_alpha) { |
138 | if (alpha != original_alpha) { |
145 | tree->pv[ply - 1] = tree->pv[ply]; |
139 | tree->pv[ply - 1] = tree->pv[ply]; |
146 | tree->pv[ply - 1].path[ply - 1] = tree->curmv[ply - 1]; |
140 | tree->pv[ply - 1].path[ply - 1] = tree->curmv[ply - 1]; |
147 | } |
141 | } |
148 | return value; |
142 | return value; |
149 | } |
143 | } |
150 | /* |
- | |
151 | ************************************************************ |
- | |
152 | * * |
- | |
153 | * This is a simple insertion sort algorithm. It seems be * |
- | |
154 | * be no faster than a normal bubble sort, but using this * |
- | |
155 | * eliminated a lot of explaining about "why?". :) * |
- | |
156 | * * |
- | |
157 | ************************************************************ |
- | |
158 | */ |
- | |
159 | if (tree->last[ply] > tree->last[ply - 1] + 1) { |
- | |
160 | int temp1, temp2, *tmovep, *tsortv, *end; |
- | |
161 | - | ||
162 | sortv = tree->sort_value + 1; |
- | |
163 |
|
144 | NextSort(tree, ply); |
164 | for (movep = tree->last[ply - 1] + 1; movep < end; movep++, sortv++) { |
- | |
165 | temp1 = *movep; |
- | |
166 | temp2 = *sortv; |
- | |
167 | tmovep = movep - 1; |
- | |
168 | tsortv = sortv - 1; |
- | |
169 | while (tmovep >= tree->last[ply - 1] && *tsortv < temp2) { |
- | |
170 | *(tsortv + 1) = *tsortv; |
- | |
171 | *(tmovep + 1) = *tmovep; |
- | |
172 | tmovep--; |
- | |
173 | tsortv--; |
- | |
174 | } |
- | |
175 | *(tmovep + 1) = temp1; |
- | |
176 | *(tsortv + 1) = temp2; |
- | |
177 | } |
- | |
178 | } |
- | |
179 | tree->next_status[ply].last = tree->last[ply - 1]; |
- | |
180 | /* |
145 | /* |
181 | ************************************************************ |
146 | ************************************************************ |
182 | * * |
147 | * * |
183 | * Iterate through the move list and search the resulting * |
148 | * Iterate through the move list and search the resulting * |
184 | * positions. Now that we are ready to actually search * |
149 | * positions. Now that we are ready to actually search * |
185 | * the set of capturing moves, we try three quick tests to * |
150 | * the set of capturing moves, we try three quick tests to * |
186 | * see if the move should be excluded because it appears * |
151 | * see if the move should be excluded because it appears * |
187 | * to lose material. * |
152 | * to lose material. * |
188 | * * |
153 | * * |
189 | * (1) If the capturing piece is not more valuable than * |
154 | * (1) If the capturing piece is not more valuable than * |
190 | * the captured piece, then the move can't lose material * |
155 | * the captured piece, then the move can't lose material * |
191 | * and should be searched. * |
156 | * and should be searched. * |
192 | * * |
157 | * * |
Line 194... | Line 159... | ||
194 | * always search this kind of capture since this can be * |
159 | * always search this kind of capture since this can be * |
195 | * the move the allows a passed pawn to promote when the * |
160 | * the move the allows a passed pawn to promote when the * |
196 | * opponent has no piece to catch it. * |
161 | * opponent has no piece to catch it. * |
197 | * * |
162 | * * |
198 | * (3) Otherwise, If the capturing piece is more valuable * |
163 | * (3) Otherwise, If the capturing piece is more valuable * |
199 | * than the captured piece, we use |
164 | * than the captured piece, we use SEE() to determine if * |
200 | * the capture is losing or not so that we don't search * |
165 | * the capture is losing or not so that we don't search * |
201 | * hopeless moves. * |
166 | * hopeless moves. * |
202 | * * |
167 | * * |
203 | ************************************************************ |
168 | ************************************************************ |
204 | */ |
169 | */ |
205 | for (next = tree->last[ply - 1]; next < tree->last[ply]; next++) { |
170 | for (next = tree->last[ply - 1]; next < tree->last[ply]; next++) { |
206 | tree->curmv[ply] = *next; |
171 | tree->curmv[ply] = Move(*next); |
207 | if (pcval[Piece(tree->curmv[ply])] > pcval[Captured(tree->curmv[ply])] && |
172 | if (pcval[Piece(tree->curmv[ply])] > pcval[Captured(tree->curmv[ply])] && |
208 | TotalPieces(wtm, occupied) |
173 | TotalPieces(Flip(wtm), occupied) |
209 | - p_vals[Captured(tree->curmv[ply])] > 0 && |
174 | - p_vals[Captured(tree->curmv[ply])] > 0 && |
210 |
|
175 | SEE(tree, wtm, tree->curmv[ply]) < 0) |
211 | continue; |
176 | continue; |
212 | #if defined(TRACE) |
177 | #if defined(TRACE) |
213 | if (ply <= trace_level) |
178 | if (ply <= trace_level) |
214 | Trace(tree, ply, 0, wtm, alpha, beta, "Quiesce", |
179 | Trace(tree, ply, 0, wtm, alpha, beta, "Quiesce", serial, CAPTURES, |
- | 180 | next - tree->last[ply - 1] + 1); |
|
215 | #endif |
181 | #endif |
216 | MakeMove(tree, ply, tree->curmv[ply] |
182 | MakeMove(tree, ply, wtm, tree->curmv[ply]); |
217 | tree->nodes_searched++; |
183 | tree->nodes_searched++; |
218 | if (!checks) |
184 | if (!checks) |
219 | value = -Quiesce(tree, |
185 | value = -Quiesce(tree, ply + 1, Flip(wtm), -beta, -alpha, 0); |
220 | else if (!Check(wtm)) { |
186 | else if (!Check(wtm)) { |
221 | if (Check(Flip(wtm))) { |
187 | if (Check(Flip(wtm))) { |
222 | tree->qchecks_done++; |
188 | tree->qchecks_done++; |
223 | value = -QuiesceEvasions(tree, |
189 | value = -QuiesceEvasions(tree, ply + 1, Flip(wtm), -beta, -alpha); |
224 | } else |
190 | } else |
225 | value = -Quiesce(tree, |
191 | value = -Quiesce(tree, ply + 1, Flip(wtm), -beta, -alpha, 0); |
226 | } |
192 | } |
227 | UnmakeMove(tree, ply, tree->curmv[ply] |
193 | UnmakeMove(tree, ply, wtm, tree->curmv[ply]); |
228 | if (abort_search || tree->stop) |
194 | if (abort_search || tree->stop) |
229 | return 0; |
195 | return 0; |
230 | if (value > alpha) { |
196 | if (value > alpha) { |
231 | if (value >= beta) |
197 | if (value >= beta) |
232 | return value; |
198 | return value; |
Line 239... | Line 205... | ||
239 | * The next block of code is only executed if the checks * |
205 | * The next block of code is only executed if the checks * |
240 | * parameter is non-zero, otherwise we skip this and exit * |
206 | * parameter is non-zero, otherwise we skip this and exit * |
241 | * with no further searching. * |
207 | * with no further searching. * |
242 | * * |
208 | * * |
243 | * Generate just the moves (non-captures) that give check * |
209 | * Generate just the moves (non-captures) that give check * |
244 | * and search the ones that |
210 | * and search the ones that SEE() says are safe. Subtle * |
245 | * trick: we discard the captures left over from the * |
211 | * trick: we discard the captures left over from the * |
246 | * above search since we labeled them "losing moves." * |
212 | * above search since we labeled them "losing moves." * |
247 | * * |
213 | * * |
248 | ************************************************************ |
214 | ************************************************************ |
249 | */ |
215 | */ |
Line 257... | Line 223... | ||
257 | * GenerateChecks() provides. * |
223 | * GenerateChecks() provides. * |
258 | * * |
224 | * * |
259 | ************************************************************ |
225 | ************************************************************ |
260 | */ |
226 | */ |
261 | for (next = tree->last[ply - 1]; next < tree->last[ply]; next++) { |
227 | for (next = tree->last[ply - 1]; next < tree->last[ply]; next++) { |
262 | tree->curmv[ply] = *next; |
228 | tree->curmv[ply] = Move(*next); |
263 | if ( |
229 | if (SEE(tree, wtm, tree->curmv[ply]) >= 0) { |
264 | #if defined(TRACE) |
230 | #if defined(TRACE) |
265 | if (ply <= trace_level) |
231 | if (ply <= trace_level) |
266 | Trace(tree, ply, 0, wtm, alpha, beta, "Quiesce", |
232 | Trace(tree, ply, 0, wtm, alpha, beta, "Quiesce+checks", serial, |
- | 233 | REMAINING, next - tree->last[ply - 1] + 1); |
|
267 | #endif |
234 | #endif |
268 | MakeMove(tree, ply, tree->curmv[ply] |
235 | MakeMove(tree, ply, wtm, tree->curmv[ply]); |
269 | tree->nodes_searched++; |
236 | tree->nodes_searched++; |
270 | if (!Check(wtm)) { |
237 | if (!Check(wtm)) { |
271 | tree->qchecks_done++; |
238 | tree->qchecks_done++; |
272 | value = -QuiesceEvasions(tree, |
239 | value = -QuiesceEvasions(tree, ply + 1, Flip(wtm), -beta, -alpha); |
273 | } |
240 | } |
274 | UnmakeMove(tree, ply, tree->curmv[ply] |
241 | UnmakeMove(tree, ply, wtm, tree->curmv[ply]); |
275 | if (abort_search || tree->stop) |
242 | if (abort_search || tree->stop) |
276 | return 0; |
243 | return 0; |
277 | if (value > alpha) { |
244 | if (value > alpha) { |
278 | if (value >= beta) |
245 | if (value >= beta) |
279 | return value; |
246 | return value; |
Line 297... | Line 264... | ||
297 | tree->pv[ply - 1].path[ply - 1] = tree->curmv[ply - 1]; |
264 | tree->pv[ply - 1].path[ply - 1] = tree->curmv[ply - 1]; |
298 | } |
265 | } |
299 | return alpha; |
266 | return alpha; |
300 | } |
267 | } |
301 | 268 | ||
302 | /* last modified |
269 | /* last modified 01/10/16 */ |
303 | /* |
270 | /* |
304 | ******************************************************************************* |
271 | ******************************************************************************* |
305 | * * |
272 | * * |
306 | * QuiesceEvasions() is the recursive routine used to implement the alpha/ * |
273 | * QuiesceEvasions() is the recursive routine used to implement the alpha/ * |
307 | * beta negamax quiescence search. The primary function here is to escape a * |
274 | * beta negamax quiescence search. The primary function here is to escape a * |
Line 314... | Line 281... | ||
314 | * in the the order they are generated since we are going to try them all * |
281 | * in the the order they are generated since we are going to try them all * |
315 | * unless we get a fail-high. * |
282 | * unless we get a fail-high. * |
316 | * * |
283 | * * |
317 | ******************************************************************************* |
284 | ******************************************************************************* |
318 | */ |
285 | */ |
319 | int QuiesceEvasions(TREE * RESTRICT tree, int |
286 | int QuiesceEvasions(TREE * RESTRICT tree, int ply, int wtm, int alpha, |
320 | int |
287 | int beta) { |
321 | int original_alpha, value; |
288 | int original_alpha, value, moves_searched = 0, order; |
322 | int moves_searched = 0; |
- | |
323 | 289 | ||
324 | /* |
290 | /* |
325 | ************************************************************ |
291 | ************************************************************ |
326 | * * |
292 | * * |
327 | * Initialize. * |
293 | * Initialize. * |
Line 329... | Line 295... | ||
329 | ************************************************************ |
295 | ************************************************************ |
330 | */ |
296 | */ |
331 | if (ply >= MAXPLY - 1) |
297 | if (ply >= MAXPLY - 1) |
332 | return beta; |
298 | return beta; |
333 | #if defined(NODES) |
299 | #if defined(NODES) |
334 | if (--temp_search_nodes <= 0) { |
300 | if (search_nodes && --temp_search_nodes <= 0) { |
335 | abort_search = 1; |
301 | abort_search = 1; |
336 | return 0; |
302 | return 0; |
337 | } |
303 | } |
338 | if (tree->thread_id == 0) |
304 | if (tree->thread_id == 0) |
339 | next_time_check--; |
305 | next_time_check--; |
Line 369... | Line 335... | ||
369 | * position. * |
335 | * position. * |
370 | * * |
336 | * * |
371 | ************************************************************ |
337 | ************************************************************ |
372 | */ |
338 | */ |
373 | tree->hash_move[ply] = 0; |
339 | tree->hash_move[ply] = 0; |
374 | tree->next_status[ply].phase = |
340 | tree->next_status[ply].phase = HASH; |
375 | while (( |
341 | while ((order = NextMove(tree, ply, 0, wtm, 1))) { |
376 | #if defined(TRACE) |
342 | #if defined(TRACE) |
377 | if (ply <= trace_level) |
343 | if (ply <= trace_level) |
378 | Trace(tree, ply, 0, wtm, alpha, beta, "QuiesceEvasions", |
344 | Trace(tree, ply, 0, wtm, alpha, beta, "QuiesceEvasions", serial, |
379 | tree->phase[ply]); |
345 | tree->phase[ply], order); |
380 | #endif |
346 | #endif |
381 | moves_searched++; |
347 | moves_searched++; |
382 | MakeMove(tree, ply, tree->curmv[ply] |
348 | MakeMove(tree, ply, wtm, tree->curmv[ply]); |
383 | tree->nodes_searched++; |
349 | tree->nodes_searched++; |
384 | value = -Quiesce(tree, |
350 | value = -Quiesce(tree, ply + 1, Flip(wtm), -beta, -alpha, 0); |
385 | UnmakeMove(tree, ply, tree->curmv[ply] |
351 | UnmakeMove(tree, ply, wtm, tree->curmv[ply]); |
386 | if (abort_search || tree->stop) |
352 | if (abort_search || tree->stop) |
387 | return 0; |
353 | return 0; |
388 | if (value > alpha) { |
354 | if (value > alpha) { |
389 | if (value >= beta) |
355 | if (value >= beta) |
390 | return value; |
356 | return value; |
Line 392... | Line 358... | ||
392 | } |
358 | } |
393 | } |
359 | } |
394 | /* |
360 | /* |
395 | ************************************************************ |
361 | ************************************************************ |
396 | * * |
362 | * * |
397 | * All moves have been searched. If none were legal, |
363 | * All moves have been searched. If none were legal, it * |
398 | * |
364 | * must be a mate since we have to be in check to reach * |
399 | * |
365 | * QuiesceEvasions(). * |
400 | * * |
366 | * * |
401 | ************************************************************ |
367 | ************************************************************ |
402 | */ |
368 | */ |
403 | if (moves_searched == 0) { |
369 | if (moves_searched == 0) { |
404 | value = |
370 | value = -(MATE - ply); |
405 | if (value >= alpha && value < beta) { |
371 | if (value >= alpha && value < beta) { |
406 | SavePV(tree, ply, 0); |
372 | SavePV(tree, ply, 0); |
407 | #if defined(TRACE) |
373 | #if defined(TRACE) |
408 | if (ply <= trace_level) |
374 | if (ply <= trace_level) |
409 | printf("Search() no moves! ply=%d\n", ply); |
375 | printf("Search() no moves! ply=%d\n", ply); |