CPG/Chess/nimfish/nimfishpkg/search.nim

74 lines
2.4 KiB
Nim

# Copyright 2024 Mattia Giambirtone & All Contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
## Implementation of negamax with a/b pruning
import board
import movegen
import eval
import std/atomics
func lowestEval*: Score {.inline.} = Score(-32000'i16)
func highestEval*: Score {.inline.} = Score(32000'i16)
func mateScore*: Score {.inline.} = lowestEval() - Score(1)
type
SearchManager* = ref object
## A simple state storage
## for our search
stopFlag*: Atomic[bool] # Can be used to cancel the search from another thread
bestMove*: Move
proc search*(self: SearchManager, board: Chessboard, depth, ply: int, alpha, beta: Score): Score {.discardable.} =
## Simple negamax search with alpha-beta pruning
if self.stopFlag.load():
# Search has been cancelled!
return
if depth == 0:
return board.evaluate()
var moves = MoveList()
board.generateMoves(moves)
if moves.len() == 0:
if board.inCheck():
# Checkmate! We add the current ply
# because mating in 3 is better than
# mating in 5 (and conversely being
# mated in 5 is better than being
# mated in 3)
return mateScore() + Score(ply)
# Stalemate
return Score(0)
var bestScore = lowestEval()
var alpha = alpha
for move in moves:
board.makeMove(move)
# Find the best move for us (worst move
# for our opponent, hence the negative sign)
let eval = -self.search(board, depth - 1, ply + 1, -beta, -alpha)
board.unmakeMove()
bestScore = max(eval, bestScore)
if eval >= beta:
# This move was too good for us, opponent will not search it
break
if eval > alpha:
alpha = eval
if ply == 0:
self.bestMove = move
return bestScore