Skip to content

Commit 3071b40

Browse files
committed
Introduce cutnodes, and perform IIR on them
ELO | 4.39 +- 3.07 (95%) SPRT | 10.0+0.10s Threads=1 Hash=8MB LLR | 2.96 (-2.94, 2.94) [0.00, 3.00] GAMES | N: 23968 W: 5986 L: 5683 D: 12299 http://chess.grantnet.us/test/31808/ ELO | 2.88 +- 2.25 (95%) SPRT | 60.0+0.60s Threads=1 Hash=64MB LLR | 2.95 (-2.94, 2.94) [0.00, 3.00] GAMES | N: 43424 W: 10527 L: 10167 D: 22730 http://chess.grantnet.us/test/31811/ BENCH : 3,583,142
1 parent f4d7770 commit 3071b40

File tree

3 files changed

+38
-29
lines changed

3 files changed

+38
-29
lines changed

src/search.c

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ void aspirationWindow(Thread *thread) {
287287
while (1) {
288288

289289
// Perform a search and consider reporting results
290-
pv.score = search(thread, &pv, alpha, beta, MAX(1, depth));
290+
pv.score = search(thread, &pv, alpha, beta, MAX(1, depth), FALSE);
291291
if ( (report && pv.score > alpha && pv.score < beta)
292292
|| (report && elapsed_time(thread->tm) >= WindowTimerMS))
293293
uciReport(thread->threads, &pv, alpha, beta);
@@ -319,7 +319,7 @@ void aspirationWindow(Thread *thread) {
319319
}
320320
}
321321

322-
int search(Thread *thread, PVariation *pv, int alpha, int beta, int depth) {
322+
int search(Thread *thread, PVariation *pv, int alpha, int beta, int depth, bool cutnode) {
323323

324324
Board *const board = &thread->board;
325325
NodeState *const ns = &thread->states[thread->height];
@@ -515,7 +515,7 @@ int search(Thread *thread, PVariation *pv, int alpha, int beta, int depth) {
515515
R = 4 + depth / 6 + MIN(3, (eval - beta) / 200) + (ns-1)->tactical;
516516

517517
apply(thread, board, NULL_MOVE);
518-
value = -search(thread, &lpv, -beta, -beta+1, depth-R);
518+
value = -search(thread, &lpv, -beta, -beta+1, depth-R, !cutnode);
519519
revert(thread, board, NULL_MOVE);
520520

521521
// Don't return unproven TB-Wins or Mates
@@ -546,7 +546,7 @@ int search(Thread *thread, PVariation *pv, int alpha, int beta, int depth) {
546546

547547
// For low depths, or after the above, verify with a reduced search
548548
if (depth < 2 * ProbCutDepth || value >= rBeta)
549-
value = -search(thread, &lpv, -rBeta, -rBeta+1, depth-4);
549+
value = -search(thread, &lpv, -rBeta, -rBeta+1, depth-4, !cutnode);
550550

551551
// Revert the board state
552552
revert(thread, board, move);
@@ -561,7 +561,15 @@ int search(Thread *thread, PVariation *pv, int alpha, int beta, int depth) {
561561
}
562562
}
563563

564-
// Step 11. Initialize the Move Picker and being searching through each
564+
// Step 11. Internal Iterative Reductions. Artifically lower the depth on cutnodes
565+
// that are high enough up in the search tree that we would expect to have found
566+
// a Transposition. This is a modernized approach to Internal Iterative Deepening
567+
if ( cutnode
568+
&& depth >= 7
569+
&& ttMove == NONE_MOVE)
570+
depth -= 1;
571+
572+
// Step 12. Initialize the Move Picker and being searching through each
565573
// move one at a time, until we run out or a move generates a cutoff. We
566574
// reuse an already initialized MovePicker to verify Singular Extension
567575
if (!ns->excluded) init_picker(&ns->mp, thread, ttMove);
@@ -581,47 +589,47 @@ int search(Thread *thread, PVariation *pv, int alpha, int beta, int depth) {
581589
hist = !isQuiet ? get_capture_history(thread, move)
582590
: get_quiet_history(thread, move, &cmhist, &fmhist);
583591

584-
// Step 12 (~80 elo). Late Move Pruning / Move Count Pruning. If we
592+
// Step 13 (~80 elo). Late Move Pruning / Move Count Pruning. If we
585593
// have seen many moves in this position already, and we don't expect
586594
// anything from this move, we can skip all the remaining quiets
587595
if ( best > -TBWIN_IN_MAX
588596
&& depth <= LateMovePruningDepth
589597
&& movesSeen >= LateMovePruningCounts[improving][depth])
590598
skipQuiets = 1;
591599

592-
// Step 13 (~175 elo). Quiet Move Pruning. Prune any quiet move that meets one
600+
// Step 14 (~175 elo). Quiet Move Pruning. Prune any quiet move that meets one
593601
// of the criteria below, only after proving a non mated line exists
594602
if (isQuiet && best > -TBWIN_IN_MAX) {
595603

596604
// Base LMR reduced depth value that we expect to use later
597605
int lmrDepth = MAX(0, depth - LMRTable[MIN(depth, 63)][MIN(played, 63)]);
598606
int fmpMargin = FutilityMarginBase + lmrDepth * FutilityMarginPerDepth;
599607

600-
// Step 13A (~3 elo). Futility Pruning. If our score is far below alpha,
608+
// Step 14A (~3 elo). Futility Pruning. If our score is far below alpha,
601609
// and we don't expect anything from this move, we can skip all other quiets
602610
if ( !inCheck
603611
&& eval + fmpMargin <= alpha
604612
&& lmrDepth <= FutilityPruningDepth
605613
&& hist < FutilityPruningHistoryLimit[improving])
606614
skipQuiets = 1;
607615

608-
// Step 13B (~2.5 elo). Futility Pruning. If our score is not only far
616+
// Step 14B (~2.5 elo). Futility Pruning. If our score is not only far
609617
// below alpha but still far below alpha after adding the Futility Margin,
610618
// we can somewhat safely skip all quiet moves after this one
611619
if ( !inCheck
612620
&& lmrDepth <= FutilityPruningDepth
613621
&& eval + fmpMargin + FutilityMarginNoHistory <= alpha)
614622
skipQuiets = 1;
615623

616-
// Step 13C (~10 elo). Continuation Pruning. Moves with poor counter
624+
// Step 14C (~10 elo). Continuation Pruning. Moves with poor counter
617625
// or follow-up move history are pruned near the leaf nodes of the search
618626
if ( ns->mp.stage > STAGE_COUNTER_MOVE
619627
&& lmrDepth <= ContinuationPruningDepth[improving]
620628
&& MIN(cmhist, fmhist) < ContinuationPruningHistoryLimit[improving])
621629
continue;
622630
}
623631

624-
// Step 14 (~42 elo). Static Exchange Evaluation Pruning. Prune moves which fail
632+
// Step 15 (~42 elo). Static Exchange Evaluation Pruning. Prune moves which fail
625633
// to beat a depth dependent SEE threshold. The use of the Move Picker's stage
626634
// is a speedup, which assumes that good noisy moves have a positive SEE
627635
if ( best > -TBWIN_IN_MAX
@@ -651,15 +659,15 @@ int search(Thread *thread, PVariation *pv, int alpha, int beta, int depth) {
651659
&& ttDepth >= depth - 3
652660
&& (ttBound & BOUND_LOWER);
653661

654-
// Step 15 (~60 elo). Extensions. Search an additional ply when the move comes from the
662+
// Step 16 (~60 elo). Extensions. Search an additional ply when the move comes from the
655663
// Transposition Table and appears to beat all other moves by a fair margin. Otherwise,
656664
// extend for any position where our King is checked.
657665

658-
extension = singular ? singularity(thread, ttMove, ttValue, depth, PvNode, alpha, beta) : inCheck;
666+
extension = singular ? singularity(thread, ttMove, ttValue, depth, PvNode, alpha, beta, cutnode) : inCheck;
659667
newDepth = depth + (!RootNode ? extension : 0);
660668
if (extension > 1) ns->dextensions++;
661669

662-
// Step 16. MultiCut. Sometimes candidate Singular moves are shown to be non-Singular.
670+
// Step 17. MultiCut. Sometimes candidate Singular moves are shown to be non-Singular.
663671
// If this happens, and the rBeta used is greater than beta, then we have multiple moves
664672
// which appear to beat beta at a reduced depth. singularity() sets the stage to STAGE_DONE
665673

@@ -668,7 +676,7 @@ int search(Thread *thread, PVariation *pv, int alpha, int beta, int depth) {
668676

669677
if (depth > 2 && played > 1) {
670678

671-
// Step 17A (~249 elo). Quiet Late Move Reductions. Reduce the search depth
679+
// Step 18A (~249 elo). Quiet Late Move Reductions. Reduce the search depth
672680
// of Quiet moves after we've explored the main line. If a reduced search
673681
// manages to beat alpha, against our expectations, we perform a research
674682

@@ -690,7 +698,7 @@ int search(Thread *thread, PVariation *pv, int alpha, int beta, int depth) {
690698
R -= MAX(-2, MIN(2, hist / 5000));
691699
}
692700

693-
// Step 17B (~3 elo). Noisy Late Move Reductions. The same as Step 17A, but
701+
// Step 18B (~3 elo). Noisy Late Move Reductions. The same as Step 18A, but
694702
// only applied to Tactical moves, based mostly on the Capture History scores
695703

696704
else {
@@ -706,7 +714,7 @@ int search(Thread *thread, PVariation *pv, int alpha, int beta, int depth) {
706714
R = MIN(depth - 1, MAX(R, 1));
707715

708716
// Perform reduced depth search on a Null Window
709-
value = -search(thread, &lpv, -alpha-1, -alpha, newDepth-R);
717+
value = -search(thread, &lpv, -alpha-1, -alpha, newDepth-R, true);
710718

711719
// Abandon searching here if we could not beat alpha
712720
doFullSearch = value > alpha && R != 1;
@@ -716,11 +724,11 @@ int search(Thread *thread, PVariation *pv, int alpha, int beta, int depth) {
716724

717725
// Full depth search on a null window
718726
if (doFullSearch)
719-
value = -search(thread, &lpv, -alpha-1, -alpha, newDepth-1);
727+
value = -search(thread, &lpv, -alpha-1, -alpha, newDepth-1, !cutnode);
720728

721729
// Full depth search on a full window for some PvNodes
722730
if (PvNode && (played == 1 || value > alpha))
723-
value = -search(thread, &lpv, -beta, -alpha, newDepth-1);
731+
value = -search(thread, &lpv, -beta, -alpha, newDepth-1, FALSE);
724732

725733
// Revert the board state
726734
revert(thread, board, move);
@@ -732,7 +740,7 @@ int search(Thread *thread, PVariation *pv, int alpha, int beta, int depth) {
732740
if (RootNode && !thread->index)
733741
thread->tm->nodes[move] += thread->nodes - starting_nodes;
734742

735-
// Step 18. Update search stats for the best move and its value. Update
743+
// Step 19. Update search stats for the best move and its value. Update
736744
// our lower bound (alpha) if exceeded, and also update the PV in that case
737745
if (value > best) {
738746

@@ -753,7 +761,7 @@ int search(Thread *thread, PVariation *pv, int alpha, int beta, int depth) {
753761
}
754762
}
755763

756-
// Step 19 (~760 elo). Update History counters on a fail high for a quiet move.
764+
// Step 20 (~760 elo). Update History counters on a fail high for a quiet move.
757765
// We also update Capture History Heuristics, which augment or replace MVV-LVA.
758766

759767
if (best >= beta && !moveIsTactical(board, bestMove))
@@ -762,17 +770,17 @@ int search(Thread *thread, PVariation *pv, int alpha, int beta, int depth) {
762770
if (best >= beta)
763771
update_capture_histories(thread, bestMove, capturesTried, capturesPlayed, depth);
764772

765-
// Step 20. Stalemate and Checkmate detection. If no moves were found to
773+
// Step 21. Stalemate and Checkmate detection. If no moves were found to
766774
// be legal then we are either mated or stalemated, For mates, return a
767775
// score based on how far or close the mate is to the root position
768776
if (played == 0) return inCheck ? -MATE + thread->height : 0;
769777

770-
// Step 21. When we found a Syzygy entry, don't report a value greater than
778+
// Step 22. When we found a Syzygy entry, don't report a value greater than
771779
// the known bounds. For example, a non-zeroing move could be played, not be
772780
// held in Syzygy, and then be scored better than the true lost value.
773781
if (PvNode) best = MAX(syzygyMin, MIN(best, syzygyMax));
774782

775-
// Step 22. Store results of search into the Transposition Table. We do not overwrite
783+
// Step 23. Store results of search into the Transposition Table. We do not overwrite
776784
// the Root entry from the first line of play we examined. We also don't store into the
777785
// Transposition Table while attempting to veryify singularities
778786
if (!ns->excluded && (!RootNode || !thread->multiPV)) {
@@ -994,7 +1002,7 @@ int staticExchangeEvaluation(Board *board, uint16_t move, int threshold) {
9941002
return board->turn != colour;
9951003
}
9961004

997-
int singularity(Thread *thread, uint16_t ttMove, int ttValue, int depth, int PvNode, int alpha, int beta) {
1005+
int singularity(Thread *thread, uint16_t ttMove, int ttValue, int depth, int PvNode, int alpha, int beta, bool cutnode) {
9981006

9991007
Board *const board = &thread->board;
10001008
NodeState *const ns = &thread->states[thread->height-1];
@@ -1007,7 +1015,7 @@ int singularity(Thread *thread, uint16_t ttMove, int ttValue, int depth, int PvN
10071015

10081016
// Search on a null rBeta window, excluding the tt-move
10091017
ns->excluded = ttMove;
1010-
value = search(thread, &lpv, rBeta-1, rBeta, (depth - 1) / 2);
1018+
value = search(thread, &lpv, rBeta-1, rBeta, (depth - 1) / 2, cutnode);
10111019
ns->excluded = NONE_MOVE;
10121020

10131021
// We reused the Move Picker, so make sure we cleanup

src/search.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#pragma once
2020

21+
#include <stdbool.h>
2122
#include <stdint.h>
2223

2324
#include "types.h"
@@ -32,10 +33,10 @@ void *start_search_threads(void *arguments);
3233
void getBestMove(Thread *threads, Board *board, Limits *limits, uint16_t *best, uint16_t *ponder, int *score);
3334
void* iterativeDeepening(void *vthread);
3435
void aspirationWindow(Thread *thread);
35-
int search(Thread *thread, PVariation *pv, int alpha, int beta, int depth);
36+
int search(Thread *thread, PVariation *pv, int alpha, int beta, int depth, bool cutnode);
3637
int qsearch(Thread *thread, PVariation *pv, int alpha, int beta);
3738
int staticExchangeEvaluation(Board *board, uint16_t move, int threshold);
38-
int singularity(Thread *thread, uint16_t ttMove, int ttValue, int depth, int PvNode, int alpha, int beta);
39+
int singularity(Thread *thread, uint16_t ttMove, int ttValue, int depth, int PvNode, int alpha, int beta, bool cutnode);
3940

4041
static const int WindowDepth = 5;
4142
static const int WindowSize = 10;

src/uci.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
#include "types.h"
2424

25-
#define VERSION_ID "14.09"
25+
#define VERSION_ID "14.10"
2626

2727
#ifndef LICENSE_OWNER
2828
#define LICENSE_OWNER "Unlicensed"

0 commit comments

Comments
 (0)