private int PrincipalVariationSearch(Board board, Colour colour, byte depth, ushort ply, int alpha, int beta, uint[] parentPrincipalVariation) { ++PositionCount; if (depth == 0) { return(positionEvaluator.Evaluate(board) * (colour == Colour.White ? 1 : -1)); } var transpositionKey = board.Key; var existingTransposition = transpositionTable.Find(transpositionKey); uint previousBestMove = 0; if (existingTransposition.Key != 0) { if (existingTransposition.Depth > 1 && existingTransposition.Depth > depth) { return(existingTransposition.Evaluation); } if (existingTransposition.BestMove != 0) { previousBestMove = existingTransposition.BestMove; } } var bestScore = int.MinValue; var bestMove = 0u; var principalVariation = new uint[64]; var moveCount = 1; foreach (var move in GetNextMove(moveGenerator, ply, board, colour, previousBestMove)) { board.MakeMove(move); var evaluatedScore = 0; var oppositeColour = colour.Opposite(); var nextDepth = (byte)(depth - 1); var nextPly = (ushort)(ply + 1); // https://www.chessprogramming.org/Principal_Variation_Search if (moveCount == 1) { // Always do a full search on the first/PV move evaluatedScore = -PrincipalVariationSearch(board, oppositeColour, nextDepth, nextPly, -beta, -alpha, principalVariation); } else { // Late Move Reduction http://mediocrechess.blogspot.com/2007/03/other-late-move-reduction-lmr.html if (ply > 3 && moveCount > 3 && move.GetCapturePieceType() == PieceType.None && move.GetNumCheckers() == 0 && nextDepth > 0) { --nextDepth; } // Search with aspiration window evaluatedScore = -PrincipalVariationSearch(board, oppositeColour, nextDepth, nextPly, -alpha - 1, -alpha, principalVariation); if (alpha < evaluatedScore && evaluatedScore < beta) { // Re-search evaluatedScore = -PrincipalVariationSearch(board, oppositeColour, nextDepth, nextPly, -beta, -evaluatedScore, principalVariation); } } board.UnMakeMove(move); if (alpha < evaluatedScore) { // alpha = Math.Max(bestScore, evaluatedScore); // alpha acts like max in MiniMax alpha = evaluatedScore; bestMove = move; bestScore = evaluatedScore; UpdatePrincipalVariation(principalVariation, parentPrincipalVariation, ply, move); } if (alpha > beta) { // Fail-hard beta-cutoff break; } ++moveCount; if (ply == 1) { var info = new Info(PositionCount, stopWatch.ElapsedMilliseconds, depth, transpositionTable); Info?.Invoke(this, new InfoEventArgs(info)); } } transpositionTable.Set(transpositionKey, depth, colour, bestScore, bestMove); if (alpha == int.MinValue) { alpha = PieceValues.CheckmateValue + ply; } if (alpha == int.MaxValue) { alpha = -(PieceValues.CheckmateValue + ply); } return(alpha); }
private int Evaluate() => positionEvaluator.Evaluate(board);