/// <summary> /// Returns the dynamic value of the position as determined by a recursive /// search that terminates upon reaching a quiescent position. /// </summary> /// <param name="position">The position to search on.</param> /// <param name="ply">The number of plies from the root position.</param> /// <param name="alpha">The lower bound on the value of the best move.</param> /// <param name="beta">The upper bound on the value of the best move.</param> /// <returns>The value of the termination position given optimal play.</returns> private Int32 Quiescence(Position position, Int32 ply, Int32 alpha, Int32 beta) { _totalNodes++; _quiescenceNodes++; // Evaluate the position statically. Check for upper bound cutoff and lower // bound improvement. Int32 value = Evaluate(position); if (value >= beta) { return(value); } if (value > alpha) { alpha = value; } // Perform hash probe. _hashProbes++; Int32 hashMove = Move.Invalid; if (_table.TryProbe(position.ZobristKey, out HashEntry hashEntry)) { Int32 hashType = hashEntry.Type; Int32 hashValue = hashEntry.GetValue(ply); if (hashType == HashEntry.Exact || (hashType == HashEntry.Beta && hashValue >= beta) || (hashType == HashEntry.Alpha && hashValue <= alpha)) { _hashCutoffs++; return(hashValue); } if (Move.IsCapture(hashEntry.Move)) { hashMove = hashEntry.Move; } } // Initialize variables and generate the pseudo-legal moves to be // considered. Perform basic move ordering and sort the moves. Int32 colour = position.SideToMove; Int32[] moves = _generatedMoves[ply]; Int32 movesCount = position.PseudoQuiescenceMoves(moves); if (movesCount == 0) { return(alpha); } for (Int32 i = 0; i < movesCount; i++) { _moveValues[i] = MoveOrderingValue(moves[i]); } // Perform hash move ordering. _hashMoveChecks++; if (hashMove != Move.Invalid) { for (Int32 i = 0; i < movesCount; i++) { if (moves[i] == hashMove) { _moveValues[i] = HashMoveValue; _hashMoveMatches++; break; } } } Sort(moves, _moveValues, movesCount); Int32 bestType = HashEntry.Alpha; Int32 bestMove = moves[0]; // Go through the move list. for (Int32 i = 0; i < movesCount; i++) { _movesSearched++; Int32 move = moves[i]; // Consider the move only if it doesn't lose material. if (EvaluateStaticExchange(position, move) >= 0) { // Make the move. position.Make(move); // Search the move if it is legal. This is equivalent to not leaving the // king in check. if (!position.InCheck(colour)) { value = -Quiescence(position, ply + 1, -beta, -alpha); // Check for upper bound cutoff and lower bound improvement. if (value >= beta) { position.Unmake(move); _table.Store(new HashEntry(position, 0, ply, move, value, HashEntry.Beta)); return(value); } if (value > alpha) { alpha = value; bestMove = move; bestType = HashEntry.Exact; } } // Unmake the move. position.Unmake(move); } } _table.Store(new HashEntry(position, 0, ply, bestMove, alpha, bestType)); return(alpha); }