Implement QSEE pruning

This commit is contained in:
Mattia Giambirtone 2024-05-11 15:08:58 +02:00
parent 25677ae2c6
commit ec309c9d4d
1 changed files with 70 additions and 29 deletions

View File

@ -187,37 +187,75 @@ proc getEstimatedMoveScore(self: SearchManager, move: Move, ply: int): int =
if move.isTactical():
let seeScore = self.board.position.see(move)
# We want to prioritize good captures (see > 0), but if the capture
# is bad then at least we sort it with MVVLVA
if seeScore < 0 and (move.isCapture() or move.isEnPassant()):
# Implementation of MVVLVA: Most Valuable Victim Least Valuable Aggressor.
# We prioritize moves that capture the most valuable pieces, and as a
# second goal we want to use our least valuable pieces to do so (this
# is why we multiply the score of the captured piece by a constant, to give
# it priority)
let capturedScore = MVV_LVA_MULTIPLIER * self.board.position.getPieceScore(move.targetSquare)
result = capturedScore - self.board.position.getPieceScore(move.startSquare)
# If the capture is also a promotion we want to give it an even bigger bonus
if move.isPromotion():
var piece: Piece
case move.getPromotionType():
of PromoteToBishop:
piece = Piece(kind: Bishop, color: sideToMove)
of PromoteToKnight:
piece = Piece(kind: Knight, color: sideToMove)
of PromoteToRook:
piece = Piece(kind: Rook, color: sideToMove)
of PromoteToQueen:
piece = Piece(kind: Queen, color: sideToMove)
else:
discard # Unreachable
result += PROMOTION_MULTIPLIER * self.board.position.getPieceScore(piece, move.targetSquare)
when not defined(SEE2):
# We want to prioritize good captures (see > 0), but if the capture
# is bad then at least we sort it with MVVLVA
if seeScore < 0 and (move.isCapture() or move.isEnPassant()):
# Implementation of MVVLVA: Most Valuable Victim Least Valuable Aggressor.
# We prioritize moves that capture the most valuable pieces, and as a
# second goal we want to use our least valuable pieces to do so (this
# is why we multiply the score of the captured piece by a constant, to give
# it priority)
let capturedScore = MVV_LVA_MULTIPLIER * self.board.position.getPieceScore(move.targetSquare)
result = capturedScore - self.board.position.getPieceScore(move.startSquare)
# If the capture is also a promotion we want to give it an even bigger bonus
if move.isPromotion():
var piece: Piece
case move.getPromotionType():
of PromoteToBishop:
piece = Piece(kind: Bishop, color: sideToMove)
of PromoteToKnight:
piece = Piece(kind: Knight, color: sideToMove)
of PromoteToRook:
piece = Piece(kind: Rook, color: sideToMove)
of PromoteToQueen:
piece = Piece(kind: Queen, color: sideToMove)
else:
discard # Unreachable
result += PROMOTION_MULTIPLIER * self.board.position.getPieceScore(piece, move.targetSquare)
return result + BAD_SEE_OFFSET
return result + BAD_SEE_OFFSET
else:
# If the capture is good then we just use the SEE score + the offset
return seeScore + GOOD_SEE_OFFSET
else:
# If the capture is good then we just use the SEE score + the offset
return seeScore + GOOD_SEE_OFFSET
# We want to prioritize good captures (see > 0) and then sort
# them with MVVLVA. Of course, good captures will be placed
# before bad captures regardless of what MVVLVA has to say
# about them
if move.isCapture() or move.isEnPassant():
# Implementation of MVVLVA: Most Valuable Victim Least Valuable Aggressor.
# We prioritize moves that capture the most valuable pieces, and as a
# second goal we want to use our least valuable pieces to do so (this
# is why we multiply the score of the captured piece by a constant, to give
# it priority)
let capturedScore = MVV_LVA_MULTIPLIER * self.board.position.getPieceScore(move.targetSquare)
result = capturedScore - self.board.position.getPieceScore(move.startSquare)
# If the capture is also a promotion we want to give it an even bigger bonus
if move.isPromotion():
var piece: Piece
case move.getPromotionType():
of PromoteToBishop:
piece = Piece(kind: Bishop, color: sideToMove)
of PromoteToKnight:
piece = Piece(kind: Knight, color: sideToMove)
of PromoteToRook:
piece = Piece(kind: Rook, color: sideToMove)
of PromoteToQueen:
piece = Piece(kind: Queen, color: sideToMove)
else:
discard # Unreachable
result += PROMOTION_MULTIPLIER * self.board.position.getPieceScore(piece, move.targetSquare)
result += seeScore
# We use >= instead of > because we're evaluating promotions by
# their SEE scores as well, which would move them all to the back
# in cases where a promotion ends up with no material loss
if seeScore >= 0:
return result + GOOD_SEE_OFFSET
else:
return result + BAD_SEE_OFFSET
if move.isQuiet():
# History heuristic bonus
@ -341,6 +379,9 @@ proc qsearch(self: var SearchManager, ply: int, alpha, beta: Score): Score =
var bestScore = score
var alpha = max(alpha, score)
for move in moves:
# Skip bad captures (gains 52.9 +/- 25.2)
if self.board.position.see(move) < 0:
continue
self.board.doMove(move)
inc(self.nodeCount)
let score = -self.qsearch(ply + 1, -beta, -alpha)