diff --git a/Chess/nimfish/nimfishpkg/search.nim b/Chess/nimfish/nimfishpkg/search.nim index 468f81e..7a84765 100644 --- a/Chess/nimfish/nimfishpkg/search.nim +++ b/Chess/nimfish/nimfishpkg/search.nim @@ -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)