Add untested FP
This commit is contained in:
parent
9b049cdcec
commit
fdf71bbfce
|
@ -51,6 +51,11 @@ const
|
||||||
# Constants to configure FP
|
# Constants to configure FP
|
||||||
# (Futility pruning)
|
# (Futility pruning)
|
||||||
|
|
||||||
|
# Limit after which FP is disabled
|
||||||
|
FP_DEPTH_LIMIT = 1
|
||||||
|
# Advantage threshold
|
||||||
|
FP_EVAL_MARGIN = 125
|
||||||
|
|
||||||
NUM_KILLERS* = 2
|
NUM_KILLERS* = 2
|
||||||
MAX_DEPTH* = 255
|
MAX_DEPTH* = 255
|
||||||
# Constants used during move ordering
|
# Constants used during move ordering
|
||||||
|
@ -158,7 +163,7 @@ func isTactical(self: Move): bool {.inline.} =
|
||||||
func isQuiet(self: Move): bool {.inline.} =
|
func isQuiet(self: Move): bool {.inline.} =
|
||||||
## Returns whether the given move is
|
## Returns whether the given move is
|
||||||
## a quiet
|
## a quiet
|
||||||
return not self.isCapture() and not self.isEnPassant()
|
return not self.isCapture() and not self.isEnPassant() and not self.isPromotion()
|
||||||
|
|
||||||
|
|
||||||
proc getEstimatedMoveScore(self: SearchManager, move: Move, ply: int): int =
|
proc getEstimatedMoveScore(self: SearchManager, move: Move, ply: int): int =
|
||||||
|
@ -376,6 +381,7 @@ proc search(self: var SearchManager, depth, ply: int, alpha, beta: Score, isPV:
|
||||||
if depth <= 0:
|
if depth <= 0:
|
||||||
# Quiescent search gain: 264.8 +/- 71.6
|
# Quiescent search gain: 264.8 +/- 71.6
|
||||||
return self.qsearch(0, alpha, beta)
|
return self.qsearch(0, alpha, beta)
|
||||||
|
let staticEval = self.board.position.evaluate()
|
||||||
if ply > 0:
|
if ply > 0:
|
||||||
# Probe the transposition table to see if we can cause an early cutoff
|
# Probe the transposition table to see if we can cause an early cutoff
|
||||||
let query = self.transpositionTable[].get(self.board.position.zobristKey, depth.uint8)
|
let query = self.transpositionTable[].get(self.board.position.zobristKey, depth.uint8)
|
||||||
|
@ -389,7 +395,6 @@ proc search(self: var SearchManager, depth, ply: int, alpha, beta: Score, isPV:
|
||||||
of UpperBound:
|
of UpperBound:
|
||||||
if query.entry.score <= alpha:
|
if query.entry.score <= alpha:
|
||||||
return query.entry.score
|
return query.entry.score
|
||||||
let staticEval = self.board.position.evaluate()
|
|
||||||
if not isPV and not self.board.inCheck() and depth <= RFP_DEPTH_LIMIT and staticEval - RFP_EVAL_THRESHOLD * depth >= beta:
|
if not isPV and not self.board.inCheck() and depth <= RFP_DEPTH_LIMIT and staticEval - RFP_EVAL_THRESHOLD * depth >= beta:
|
||||||
## Reverse futility pruning: if the side to move has a significant advantage
|
## Reverse futility pruning: if the side to move has a significant advantage
|
||||||
## in the current position and is not in check, return the position's static
|
## in the current position and is not in check, return the position's static
|
||||||
|
@ -463,6 +468,14 @@ proc search(self: var SearchManager, depth, ply: int, alpha, beta: Score, isPV:
|
||||||
for i, move in moves:
|
for i, move in moves:
|
||||||
if ply == 0 and self.searchMoves.len() > 0 and move notin self.searchMoves:
|
if ply == 0 and self.searchMoves.len() > 0 and move notin self.searchMoves:
|
||||||
continue
|
continue
|
||||||
|
when defined(FP):
|
||||||
|
if not isPV and depth <= FP_DEPTH_LIMIT and staticEval + FP_EVAL_MARGIN * depth < alpha and bestScore > mateScore() - MAX_DEPTH:
|
||||||
|
# Futility pruning: If a move cannot meaningfully improve alpha, prune it from the
|
||||||
|
# tree. Much like RFP, this is an unsound optimization (and a riskier one at that,
|
||||||
|
# apparently), so our depth limit and evaluation margins are very conservative
|
||||||
|
# compared to RFP. Also, we need to make sure the best score is not a mate score, or
|
||||||
|
# we'd risk pruning moves that evade checkmate
|
||||||
|
continue
|
||||||
self.board.doMove(move)
|
self.board.doMove(move)
|
||||||
let
|
let
|
||||||
extension = self.getSearchExtension(move)
|
extension = self.getSearchExtension(move)
|
||||||
|
@ -508,12 +521,12 @@ proc search(self: var SearchManager, depth, ply: int, alpha, beta: Score, isPV:
|
||||||
bestScore = max(score, bestScore)
|
bestScore = max(score, bestScore)
|
||||||
if score >= beta:
|
if score >= beta:
|
||||||
if move.isQuiet():
|
if move.isQuiet():
|
||||||
# History heuristic: keep track of moves that caused a beta cutoff and order
|
# History heuristic: keep track of quiets that caused a beta cutoff and order
|
||||||
# them early in subsequent searches, as they might be really good later. A
|
# them early in subsequent searches, as they might be really good later. A
|
||||||
# quadratic bonus wrt. depth is usually the value that is used (though some
|
# quadratic bonus wrt. depth is usually the value that is used (though some
|
||||||
# engines, namely Stockfish, use a linear bonus. Maybe we can investigate this)
|
# engines, namely Stockfish, use a linear bonus. Maybe we can investigate this)
|
||||||
self.storeHistoryScore(sideToMove, move, score, depth * depth)
|
self.storeHistoryScore(sideToMove, move, score, depth * depth)
|
||||||
# Killer move heuristic: store moves that caused a beta cutoff according to the distance from
|
# Killer move heuristic: store quiets that caused a beta cutoff according to the distance from
|
||||||
# root that they occurred at, as they might be good refutations for future moves from the opponent.
|
# root that they occurred at, as they might be good refutations for future moves from the opponent.
|
||||||
# Elo gains: 33.5 +/- 19.3
|
# Elo gains: 33.5 +/- 19.3
|
||||||
self.storeKillerMove(ply, move)
|
self.storeKillerMove(ply, move)
|
||||||
|
|
Loading…
Reference in New Issue