Skip to content

Commit f3ce809

Browse files
feat: first pass at RFP
bench: 760228
1 parent 67180f3 commit f3ce809

File tree

2 files changed

+48
-2
lines changed

2 files changed

+48
-2
lines changed

engine/src/search.rs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* Created Date: Thursday, November 21st 2024
55
* Author: Paul Tsouchlos (DeveloperPaul123) ([email protected])
66
* -----
7-
* Last Modified: Wed Dec 18 2024
7+
* Last Modified: Tue Mar 25 2025
88
* -----
99
* Copyright (c) 2024 Paul Tsouchlos (DeveloperPaul123)
1010
* GNU General Public License v3.0 or later
@@ -33,6 +33,7 @@ use crate::{
3333
score::{LargeScoreType, Score, ScoreType},
3434
traits::Eval,
3535
ttable::{self, TranspositionTableEntry},
36+
tuneable::{MAX_RFP_DEPTH, RFP_MARGIN},
3637
};
3738
use ttable::TranspositionTable;
3839

@@ -345,6 +346,11 @@ impl<'a> Search<'a> {
345346
}
346347
}
347348

349+
// can we prune the current node with something other than TT?
350+
if let Some(score) = self.pruned_score(board, depth, ply, alpha, beta) {
351+
return score;
352+
}
353+
348354
// get all legal moves
349355
let mut move_list = MoveList::new();
350356
self.move_gen.generate_legal_moves(board, &mut move_list);
@@ -462,6 +468,43 @@ impl<'a> Search<'a> {
462468
best_score
463469
}
464470

471+
/// Checks to see if the current node can be pruned. If it can, returns the score. Otherwise returns None.
472+
///
473+
/// # Arguments
474+
///
475+
/// - `board` - The current board state.
476+
/// - `depth` - The current depth.
477+
/// - `beta` - The current beta value.
478+
///
479+
/// # Returns
480+
///
481+
/// The score of the position if it can be pruned, otherwise None.
482+
fn pruned_score(
483+
&self,
484+
board: &Board,
485+
depth: i16,
486+
ply: i16,
487+
alpha: Score,
488+
beta: Score,
489+
) -> Option<Score> {
490+
let is_pv_node = ply == 0 || beta - alpha > Score::new(1);
491+
492+
// no pruning if we are in check or if we are in a PV node
493+
if board.is_in_check(&self.move_gen) || is_pv_node {
494+
return None;
495+
}
496+
497+
let static_eval = self.eval.eval(board);
498+
// Reverse futility pruning
499+
// https://cosmo.tardis.ac/files/2023-02-20-viri-wiki.html
500+
// https://www.chessprogramming.org/Reverse_Futility_Pruning
501+
// If the static evaluation is very high and beats beta by a depth-dependent margin, we can prune the move.
502+
if depth <= MAX_RFP_DEPTH && static_eval - RFP_MARGIN * depth > beta {
503+
return Some(static_eval);
504+
}
505+
None
506+
}
507+
465508
/// Implements [quiescence search](https://www.chessprogramming.org/Quiescence_Search).
466509
/// We use this to avoid the horizon effect. The idea is to evaluate quiet moves where there are no tactical moves to make.
467510
///

engine/src/tuneable.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* Created Date: Wednesday, December 11th 2024
55
* Author: Paul Tsouchlos (DeveloperPaul123) ([email protected])
66
* -----
7-
* Last Modified: Thu Dec 12 2024
7+
* Last Modified: Tue Mar 25 2025
88
* -----
99
* Copyright (c) 2024 Paul Tsouchlos (DeveloperPaul123)
1010
* GNU General Public License v3.0 or later
@@ -16,3 +16,6 @@ use crate::score::ScoreType;
1616

1717
pub(crate) const MIN_ASPIRATION_DEPTH: ScoreType = 1;
1818
pub(crate) const ASPIRATION_WINDOW: ScoreType = 50;
19+
20+
pub(crate) const MAX_RFP_DEPTH: ScoreType = 4;
21+
pub(crate) const RFP_MARGIN: ScoreType = 82;

0 commit comments

Comments
 (0)